Search code examples
androidlistviewscrollaccelerometer

Android listactivity scrolling with accelerometer issue


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?


Solution

  • 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