Listview中的nullpointer - getChildAt(getFirstVisiblePosition())

时间:2011-05-02 20:54:10

标签: android

我有一个带有全屏图片的Listview。 (就像一个垂直的画廊)。我的问题是我想要覆盖onTouchEvent的listviews,这样它一次只能改变一张图片,所以在滚动之后(当没有发生投掷时),选择的图片会自行居中。

我的问题是,当我调用getChildAt(getFirstVisiblePosition())时,它返回null。我已经检查过并且getFirstVisiblePosition()正在返回一个目前可见的视图。

我正在使用smoothScrollToPosition使图片居中。

我的代码很长,但重要的部分是:(收到MotionEvent.ACTION_UP时)。

case MotionEvent.ACTION_UP:
{
    try
    {
        //Calculetes the velocity if the movement
            int initialVelocity = (int)ModifiedVelocityTracker.getYVelocity();*/
        if (mVelocityTracker == null) 
        {
            mVelocityTracker = VelocityTracker.obtain();
        }
        final VelocityTracker ModifiedVelocityTracker = mVelocityTracker;
        ModifiedVelocityTracker.computeCurrentVelocity(1000);
        int initialVelocity = (int)ModifiedVelocityTracker.getYVelocity();

        //Detects if the motion is a fling
        if ((Math.abs(initialVelocity) >
        ViewConfiguration.get(mContext).getScaledMinimumFlingVelocity()) &&
        (getChildCount() > 0)) // TODO FLing
        {
            super.onTouchEvent(getEventWithACTION_MOVE(ev));

            //Send the events to cancel the superclass fling
            //MotionEvent me[] = mVelocityTracker.getTrickEvents();
            MotionEvent me[] = getTrickEvents(ev);
            for(int i=0;i<me.length;i++)
                super.onTouchEvent(me[i]);

            int firstVisiblePosition = getFirstVisiblePosition();

            // Pint y=0 is located on the bottom, so a negative speed means
            //the finger moved up and the picture to come is the one under
            if(initialVelocity < 0) //EventUp-GoDown
            {
                if(firstVisiblePosition != getCount() - 1) //Can Move
                    {
                        //smoothScrollToPosition(firstVisiblePosition + 1);
                        View currentTopView = getChildAt(firstVisiblePosition);
                        int botom = currentTopView.getBottom();
                        botom = botom - firstVisiblePosition * getHeight();
                        smoothScrollBy(-botom, 500);
                    } else
                    {
                            smoothScrollToPosition(firstVisiblePosition);
                    }
            } else //EventDown-GoUp
            {
                smoothScrollToPosition(firstVisiblePosition);
            }
        }
        else // TODO Stay in the picture that has a bigger area shown
        { 
            onTouchEvent = super.onTouchEvent(ev);

            //Center The View after the parent stops moving it

                        /*
                         *   FIREST VIEW |****| / SECOND VIEW |    |
                         * 
                         *  CASE 1 Bottom over center - Set second view the main one
                         *   |****|
                         *   |****| Bottom of Top View at First visible Position
                         *   |    |
                         *   |----| center line
                         *   |    |
                         *   |    |
                         *   |    |
                         * 
                         * CASE 2 Bottom under center - Set first view the main one
                         *   |****|
                         *   |****|
                         *   |****|
                         *   |----| center line
                         *   |****|
                         *   |****| Bottom of Top View at First visible Position
                         *   |    |
                         * */

            int firstVisiblePosition = getFirstVisiblePosition();
            View currentTopView = getChildAt(firstVisiblePosition);
            int botom;
            if(currentTopView != null)
            {
                botom = currentTopView.getBottom();
            }else{
                currentTopView = getSelectedView();
                if(firstVisiblePosition == getPositionForView(currentTopView))
                {
                    botom = currentTopView.getBottom();
                }
                else
                {
                    botom = currentTopView.getTop();
                }
            }
            int center = getHeight()/2;
            botom = botom - firstVisiblePosition * getHeight();
            if(botom < center) //Case 1 - Scroll Down
            {
                //Checks if the top view is the last one.
                    //Shouldn't happen, but just in case.
                if(firstVisiblePosition != getCount() - 1) //Can Move
                {
                    smoothScrollToPosition(firstVisiblePosition + 1);

                } else
                {
                    smoothScrollToPosition(firstVisiblePosition);
                }
            }
            else //Case 2
            {
                smoothScrollToPosition(firstVisiblePosition);
            }
        }
        onTouchEvent = true;
    } catch(NullPointerException e)
    {
        e.printStackTrace();
    }

}

提前坦克。


由于评论。

    import android.content.Context;
import android.os.SystemClock;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.ListView;

public class FixedListView extends ListView{


    final static String LOGTAG = "TEST";

    private Context mContext;
    VelocityTracker mVelocityTracker;

    public FixedListView(Context context) {
        super(context);
        mContext = context;
        setDivider(null);
        // TODO Auto-generated constructor stub
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) 
    {
        boolean onTouchEvent = true;

        final int action = ev.getAction();

        //Add the event to the ModifiedVelocityTracker used for detect if if the motion is a fling (also creates it)
        Log.d(LOGTAG, "Before add to Traker: " + SystemClock.uptimeMillis());
        /*if (mVelocityTracker == null) 
        {
            mVelocityTracker = ModifiedVelocityTracker.obtain();
        }
        mVelocityTracker.addMovement(ev);*/
        Log.d(LOGTAG, "After add to Traker: " + SystemClock.uptimeMillis());
        //Detect the event action type
        switch (action) {
            case MotionEvent.ACTION_DOWN: 
            case MotionEvent.ACTION_MOVE: 
            {
                Log.d(LOGTAG, "After eval action Traker: " + SystemClock.uptimeMillis());
                onTouchEvent = super.onTouchEvent(ev);
                Log.d(LOGTAG, "After super Y = " + ev.getY()  +": " + SystemClock.uptimeMillis());
                break;
            }
            case MotionEvent.ACTION_UP:
            {
                try
                {
                    //Calculetes the velocity if the movement
                    //NOTE: I'm using my own VelocityTraker because the Android.Utils
                    //one can only be used once at a time and the superclass uses it.
                    /*final ModifiedVelocityTracker ModifiedVelocityTracker = mVelocityTracker;
                    ModifiedVelocityTracker.computeCurrentVelocity(1000);
                    int initialVelocity = (int)ModifiedVelocityTracker.getYVelocity();*/
                    if (mVelocityTracker == null) 
                    {
                        mVelocityTracker = VelocityTracker.obtain();
                    }
                    final VelocityTracker ModifiedVelocityTracker = mVelocityTracker;
                    ModifiedVelocityTracker.computeCurrentVelocity(1000);
                    int initialVelocity = (int)ModifiedVelocityTracker.getYVelocity();

                    //Detects if the motion is a fling
                    if ((Math.abs(initialVelocity) >
                    ViewConfiguration.get(mContext).getScaledMinimumFlingVelocity()) &&
                    (getChildCount() > 0)) // TODO FLing
                    {
                        super.onTouchEvent(getEventWithACTION_MOVE(ev));

                        //Send the events to cancel the superclass fling
                        //MotionEvent me[] = mVelocityTracker.getTrickEvents();
                        MotionEvent me[] = getTrickEvents(ev);
                        for(int i=0;i<me.length;i++)
                            super.onTouchEvent(me[i]);

                        int firstVisiblePosition = getFirstVisiblePosition();

                        // Pint y=0 is located on the bottom, so a negative speed means
                        //the finger moved up and the picture to come is the one under
                        if(initialVelocity < 0) //EventUp-GoDown
                        {
                            if(firstVisiblePosition != getCount() - 1) //Can Move
                            {
                                //smoothScrollToPosition(firstVisiblePosition + 1);
                                View currentTopView = getChildAt(firstVisiblePosition);
                                int botom = currentTopView.getBottom();
                                botom = botom - firstVisiblePosition * getHeight();
                                smoothScrollBy(-botom, 500);
                            } else
                            {
                                smoothScrollToPosition(firstVisiblePosition);
                            }
                        } else //EventDown-GoUp
                        {
                                smoothScrollToPosition(firstVisiblePosition);
                        }
                    }
                    else // TODO Stay in the picture that has a bigger area shown
                    { 
                        onTouchEvent = super.onTouchEvent(ev);

                        //Center The View after the parent stops moving it

                        /*
                         *   FIREST VIEW |****| / SECOND VIEW |    |
                         * 
                         *  CASE 1 Bottom over center - Set second view the main one
                         *   |****|
                         *   |****| Bottom of Top View at First visible Position
                         *   |    |
                         *   |----| center line
                         *   |    |
                         *   |    |
                         *   |    |
                         * 
                         * CASE 2 Bottom under center - Set first view the main one
                         *   |****|
                         *   |****|
                         *   |****|
                         *   |----| center line
                         *   |****|
                         *   |****| Bottom of Top View at First visible Position
                         *   |    |
                         * */

                        int firstVisiblePosition = getFirstVisiblePosition();
                        View currentTopView = getChildAt(firstVisiblePosition);
                        int botom;
                        if(currentTopView != null)
                        {
                            botom = currentTopView.getBottom();
                        }else{
                            currentTopView = getSelectedView();
                            if(firstVisiblePosition == getPositionForView(currentTopView))
                            {
                                botom = currentTopView.getBottom();
                            }
                            else
                            {
                                botom = currentTopView.getTop();
                            }
                        }
                        int center = getHeight()/2;
                        botom = botom - firstVisiblePosition * getHeight();
                        if(botom < center) //Case 1 - Scroll Down
                        {
                            //Checks if the top view is the last one.
                            //Shouldn't happen, but just in case.
                            if(firstVisiblePosition != getCount() - 1) //Can Move
                            {
                                smoothScrollToPosition(firstVisiblePosition + 1);

                            } else
                            {
                                smoothScrollToPosition(firstVisiblePosition);
                            }
                        }
                        else //Case 2
                        {

                            smoothScrollToPosition(firstVisiblePosition);
                        }
                    }
                    onTouchEvent = true;
                } catch(NullPointerException e)
                {
                    e.printStackTrace();
                }
                if(mVelocityTracker != null)
                    mVelocityTracker.recycle();
                break;
            }
            case MotionEvent.ACTION_CANCEL:
            {
                try
                {
                    onTouchEvent = super.onTouchEvent(ev);

                    //Center The View after the parent stops moving it

                    /*
                     *   FIREST VIEW |****| / SECOND VIEW |    |
                     * 
                     *  CASE 1 Bottom over center - Set second view the main one
                     *   |****|
                     *   |****| Bottom of Top View at First visible Position
                     *   |    |
                     *   |----| center line
                     *   |    |
                     *   |    |
                     *   |    |
                     * 
                     * CASE 2 Bottom under center - Set first view the main one
                     *   |****|
                     *   |****|
                     *   |****|
                     *   |----| center line
                     *   |****|
                     *   |****| Bottom of Top View at First visible Position
                     *   |    |
                     * */

                    int firstVisiblePosition = getFirstVisiblePosition();
                    View currentTopView = getChildAt(firstVisiblePosition);
                    int center = getHeight()/2;
                    if(currentTopView.getBottom() < center) //Case 1 - Scroll Down
                    {
                        //Checks if the top view is the last one.
                        //Shouldn't happen, but just in case.
                        if(firstVisiblePosition != getCount() - 1) //Can Move
                        {
                            smoothScrollToPosition(firstVisiblePosition + 1);
                        } else
                        {
                            smoothScrollToPosition(firstVisiblePosition);
                        }
                    }
                    else //Case 2
                    {
                        if(firstVisiblePosition != 0) //Can Move
                        {
                            smoothScrollToPosition(firstVisiblePosition - 1);
                        } else
                        {
                            smoothScrollToPosition(firstVisiblePosition);
                        }
                    }
                    onTouchEvent = true;
                } catch(NullPointerException e)
                {
                    e.printStackTrace();
                }
                if(mVelocityTracker != null)
                {
                    mVelocityTracker.recycle();
                    mVelocityTracker = null;
                }
            }
         }

        // TODO Auto-generated method stub
        return onTouchEvent;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        // TODO Auto-generated method stub
        return super.onInterceptTouchEvent(ev);
    }


    public MotionEvent[] getTrickEvents(MotionEvent ev)
    {
        //Detect the last touched position
        final float requiredX = ev.getX();
        final float requiredY = ev.getY();

        //Get a time value that is longer than the last added event value by a bigger
        //number than the LONGEST_PAST_TIME accepted by the VelocityTraker
        //NOTE: If GOOGLE changes the LONGEST_PAST_TIME, we will have to change it too,
        //I wasn't able to retrieve the original VelocityTracker LONGEST_PAST_TIME directly from it.
        final long requiredPastTime = ev.getEventTime() + 201;

        //Create the MotionEvents (Simulating no movement in y).
        MotionEvent m1 = null;
        if(requiredX == 0) //If at the left border, move one pixel to the right.
        {
            m1 = MotionEvent.obtain(requiredPastTime, requiredPastTime, 
                    MotionEvent.ACTION_MOVE,
                    requiredX + 1, requiredY, 0);
        }
        else //If not at the left border, move one pixel to the left
        {
            m1 = MotionEvent.obtain(requiredPastTime, requiredPastTime, 
                    MotionEvent.ACTION_MOVE,
                    requiredX - 1, requiredY, 0);
        }
        //Return to the original position after 100 time units
        MotionEvent m2 = MotionEvent.obtain(requiredPastTime + 100, requiredPastTime + 100, 
                MotionEvent.ACTION_UP,
                requiredX, requiredY, 0);
       MotionEvent motEvents[] = {m1,m2};

       return motEvents;
    }

    public MotionEvent getEventWithACTION_MOVE(MotionEvent ev)
    {
        //Detect the last touched position
        final float requiredX = ev.getX();
        final float requiredY = ev.getY();

        //Get a time value that is longer than the last added event value by a bigger
        //number than the LONGEST_PAST_TIME accepted by the VelocityTraker
        //NOTE: If GOOGLE changes the LONGEST_PAST_TIME, we will have to change it too,
        //I wasn't able to retrieve the original VelocityTracker LONGEST_PAST_TIME directly from it.
        final long Time = ev.getEventTime();

        //Create the MotionEvent
        MotionEvent m1 = MotionEvent.obtain(Time, Time, 
                    MotionEvent.ACTION_MOVE,
                    requiredX , requiredY, 0);

       return m1;
        }
    }

和适配器

public class FixedListAdapter extends BaseAdapter
{
    int pictures[];

    public FixedListAdapter(int pictures[])
    {
        this.pictures = pictures;
    }

    public int getCount() {
        return pictures.length;
    }

    public Object getItem(int position) {
        return pictures[position];
    }

    public long getItemId(int position) {
        return pictures[position];
    }

    public View getView(int position, View convertView, ViewGroup parent) {
        LinearLayout l = new LinearLayout(GalleryTest.this);
        l.setLayoutParams(new ListView.LayoutParams(
                ListView.LayoutParams.MATCH_PARENT, ListView.LayoutParams.MATCH_PARENT));
        l.setGravity(Gravity.CENTER);
        l.setPadding(0,0,0,0);
        l.setBackgroundColor(Color.BLACK);

        ImageView i = new ImageView(GalleryTest.this);
        i.setLayoutParams(new LinearLayout.LayoutParams(width, height));
        i.setScaleType(ImageView.ScaleType.FIT_CENTER);
        i.setImageResource(pictures[position]);

        l.addView(i);
        return l;
    }



}

注意:图片在设备上发生变化,getFirstVisiblePosition()给出了显示的第一张图片的索引。问题是getChildAt返回null,并且实际显示在屏幕上。

1 个答案:

答案 0 :(得分:1)

解决了我的问题。

我没有找到问题的原因,但是稍微更改了代码我就试图解决这个问题。

    int firstVisiblePosition = getFirstVisiblePosition();

View currentTopView = getChildAt(firstVisiblePosition);
int transpose = 0;

while(currentTopView == null)
{
    transpose++;
    currentTopView = getChildAt(firstVisiblePosition - transpose);
}
int botom = currentTopView.getBottom();
int top = currentTopView.getTop();
int height = getHeight();
botom = botom + height * (transpose - firstVisiblePosition);
top = top + height * (transpose - firstVisiblePosition);

我所做的是检查视图是否为空(即使它被显示)。如果它为null,我检查它上面的视图是否为null,依此类推,直到我在getChildAt(position)上找到一个不为null的视图。

当我找到它时,我用它的位置来计算另一个视图的位置。

我还稍微更改了代码以使用SmoothScrollBy insted os SmoothScrollToPosition,因为它使视图以这种方式完美居中。

现在,如果有人找到问题的原因,请告诉我它为什么会发生。