1

I have a ListActivity in my android application that works perfectly. Until I decided to add scrolling capabilities by using the accelerometer.

Here's my code:

package be.pxl.minecraftguide;

import android.app.ListActivity;
import android.content.ContentResolver;
import android.database.Cursor;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.support.v4.widget.SimpleCursorAdapter;
import android.widget.ListView;
import be.pxl.minecraftguide.providers.RecipeCategoryProvider;

public class Crafting extends ListActivity implements SensorEventListener {
    private SimpleCursorAdapter adapter;
    private SensorManager sensorManager;
    private Sensor acceleroMeter;
    private float[] history = new float[2];

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);     
        setContentView(R.layout.categorylist);

        ContentResolver cr = getContentResolver();
        Cursor cursor = cr.query(RecipeCategoryProvider.CONTENT_URI, null, null, null, null);
        String[] from = {RecipeCategoryProvider.COL_CATID, RecipeCategoryProvider.COL_CATIMG, RecipeCategoryProvider.COL_CATDESC};
        int[] to = { R.id.txtCatID, R.id.imgCatImage, R.id.txtCatDescription };
        adapter = new SimpleCursorAdapter(getApplicationContext(), R.layout.categoryrow, cursor, from, to, 0);
        /*SimpleCursorAdapter.ViewBinder viewBinder = new SimpleCursorAdapter.ViewBinder() {

            @Override
            public boolean setViewValue(View view, Cursor cursor, int arg2) {
                if(view.getId() == R.id.imgCatImage) {
                    ImageView image = (ImageView)findViewById(R.id.imgCatImage);
                    image.setImageResource(R.drawable.ic_launcher);
                }

                return false;
            }

        };*/

        setListAdapter(adapter);

        //__________BRON/ http://stackoverflow.com/questions/18751878/android-using-the-accelerometer-to-create-a-simple-maraca-app_____________

        sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
        acceleroMeter = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        sensorManager.registerListener(this, acceleroMeter, 1000000);

        /*recipeListView.setOnClickListener(new OnItemClickListener(){
            public void onItemClick(AdapterView<?> parent, View row,
                    int position, long id) {

                    TextView txtId = (TextView)row.findViewById(R.id.txtRecipeID);
                    int recipeID = Integer.parseInt(txtId.getText().toString());
                    String description = ((TextView)row.findViewById(R.id.txtRecipeDescription)).getText().toString();


                    // Launching new Activity on selecting single List Item
                    // Intent craftingDetailIntent = new Intent(getApplicationContext(), craftingdetail.class);
                    // sending data to new activity
                    //craftingDetailIntent.putExtra("Category", item);
                    //startActivity(craftingDetailIntent); 
                }
        });*/
    }

    /*@Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        // TODO Auto-generated method stub
        super.onListItemClick(l, v, position, id);

        int recipeID = Integer.parseInt(v.getTag().toString());
    }*/

    @Override
    protected void onPause() {
        // TODO Auto-generated method stub
        super.onPause();
        sensorManager.unregisterListener(this); //De accelerometer afzetten
    }

    @Override
    protected void onResume() {
        // TODO Auto-generated method stub
        super.onResume();
        sensorManager.registerListener(this, acceleroMeter, SensorManager.SENSOR_DELAY_UI); //De accelerometer opnieuw starten
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
        // TODO Auto-generated method stub

    }

    @Override
    public void onSensorChanged(SensorEvent event) {
        if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
            float xChange = history[0] - event.values[0];
            float yChange = history[1] - event.values[1]; //Verschil tussen nieuwe en oude positie ophalen.

            history[0] = event.values[0];
            history[1] = event.values[1]; //Nieuwe waarden bewaren voor volgende event trigger

            if (xChange > 2){
                //Links
            }
            else if (xChange < -2){
                //Rechts
            }

            if (yChange > 2){
                    getListView().smoothScrollBy(getListView().getHeight() * adapter.getCount(), 2000);
                    getListView().postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            getListView().smoothScrollBy(0, 0); //Geanimeerd scrollen naar laatste positie
                            getListView().setSelection(adapter.getCount() - 1);
                        }
                    }, 1000);
            }
            else if (yChange < -2){
                getListView().smoothScrollBy(getListView().getHeight() * adapter.getCount(), 2000);
                getListView().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        getListView().smoothScrollBy(0, 0); //Geanimeerd scrollen naar eerste positie positie
                        getListView().setSelection(0);
                    }
                }, 1000);
            }
        }

    }


}

The problem is, when I make an upward movement, the list should scroll down ad stay at the bottom. But my list scrolls down and then back up occasionally. Same goes for downward movements.

<?xml version="1.0" encoding="utf-8"?>
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/list"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/minecraft_portrait"
    android:transcriptMode="alwaysScroll" >


</ListView>

And not really relevant, but here's my row xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/recipeRow"
    android:layout_width="match_parent"
    android:layout_height="65sp"
    android:background="@drawable/listview_selector" >

    <TextView
        android:id="@+id/txtCatID"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="5dp"
        android:layout_marginTop="5dp"
        android:textColor="@android:color/black" />

    <ImageView
        android:id="@+id/imgCatImage"
        android:layout_width="wrap_content"
        android:layout_height="55sp"
        android:layout_centerVertical="true"
        android:layout_alignParentLeft="true"
        android:src="@drawable/ic_launcher"
        android:contentDescription="@string/recipecategory" />

    <TextView
        android:id="@+id/txtCatDescription"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_toRightOf="@id/imgCatImage"
        android:gravity="center"
        android:background="#99FFFFFF"
        android:textSize="20sp"
        android:textColor="@android:color/black"
        android:text="test" />

</RelativeLayout>

How can I fix this?

DerpyNerd
  • 4,743
  • 7
  • 41
  • 92

2 Answers2

2

I was looking for the same solution for scrolling a ListActivity using sensors (in context of Google Glass), and the other answer didn't work for me (scrolling was erratic).

I was able to get another version working though, based just on the ListView (mList, in the example below). Note that this also assumes a fixed number of items on the screen at one time (since all Glass displays are the same size), but this could be changed to check how many items are on the screen for a more general Android solution. This solution also uses the Sensor.TYPE_ROTATION_VECTOR, which does some sensor fusion on supported devices, and therefore has a less noisy output (resulting in less unwanted up/down movement).

Register the listener:

mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
    mSensorManager.registerListener(this,
        mSensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR),
        SensorManager.SENSOR_DELAY_UI);

...and here's the onSensorChanged() method:

@Override
public void onSensorChanged(SensorEvent event) {
    if (mList == null) {
        return;
    }

    if (event.sensor.getType() == Sensor.TYPE_ROTATION_VECTOR) {
        SensorManager.getRotationMatrixFromVector(mRotationMatrix, event.values);

        // Only uncomment the below two lines if you're running on Google Glass
        //SensorManager.remapCoordinateSystem(mRotationMatrix, SensorManager.AXIS_X,
        //    SensorManager.AXIS_Z, mRotationMatrix);
        SensorManager.getOrientation(mRotationMatrix, mOrientation);

        mHeading = (float) Math.toDegrees(mOrientation[0]);
        mPitch = (float) Math.toDegrees(mOrientation[1]);

        float xDelta = history[0] - mHeading;  // Currently unused
        float yDelta = history[1] - mPitch;

        history[0] = mHeading;
        history[1] = mPitch;

        float Y_DELTA_THRESHOLD = 0.10f;

        //Log.d(TAG, "Y Delta = " + yDelta);

        int scrollHeight = mList.getHeight()
            / 19; // 4 items per page, scroll almost 1/5 an item

        //Log.d(TAG, "ScrollHeight = " + scrollHeight);

        if (yDelta > Y_DELTA_THRESHOLD) {
            //Log.d(TAG, "Detected change in pitch up...");
            mList.smoothScrollBy(-scrollHeight, 0);
        } else if (yDelta < -Y_DELTA_THRESHOLD) {
            //Log.d(TAG, "Detected change in pitch down...");
            mList.smoothScrollBy(scrollHeight, 0);
        }
    }
}

Additional details are in this answer: https://stackoverflow.com/a/23298377/937715

For code that remaps the sensor coordinate system to handle all possible orientation changes on Android devices, see: https://stackoverflow.com/a/22138449/937715

Community
  • 1
  • 1
Sean Barbeau
  • 11,496
  • 8
  • 58
  • 111
  • 2
    While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. – Audrius Meškauskas Apr 25 '14 at 17:12
  • @AudriusMeškauskas Ok, thanks. I just updated the answer with the essential parts in-line. – Sean Barbeau Apr 25 '14 at 17:50
  • 1
    Hi sean, it's nice to see that there's a cleaner solution to this. My dirty solution did work, but it was not ideal! I hope people with a similar issue find your solution to be worth it. – DerpyNerd Apr 26 '14 at 15:50
  • @robbie Thanks! Thanks for posting your question/solution, as it definitely got me pointed in the right direction. It was the only example I found for scrolling a listview using sensors! – Sean Barbeau Apr 26 '14 at 16:04
0

Alright, I fixed this problem a few days ago.

If someone else is experiencing this, it might help to increase the change in the Y-axis.

if (yChange > 12){
        listView.smoothScrollBy(listView.getHeight() * adaptor.getCount(), 1000);
        listView.postDelayed(new Runnable() {
            @Override
            public void run() {
                listView.smoothScrollBy(0, 0); //Geanimeerd scrollen naar laatste positie
                listView.setSelection(adaptor.getCount() - 1);
            }
        }, 500);
}
else if (yChange < -12){
    listView.smoothScrollBy(listView.getHeight() * adaptor.getCount(), 1000);
    listView.postDelayed(new Runnable() {
        @Override
        public void run() {
            listView.smoothScrollBy(0, 0); //Geanimeerd scrollen naar eerste positie positie
            listView.setSelection(0);
        }
    }, 500);
}
DerpyNerd
  • 4,743
  • 7
  • 41
  • 92