簡體   English   中英

ListView行中的ViewPager可防止觸發onItemClick

[英]ViewPager inside ListView row prevents onItemClick to be fired

我在ListView的每一行中都有一個ViewPager。 它工作正常,當用戶使用滑動手勢時,它會更改其中的視圖,但它會阻止調用ListView的onItemClick方法。 我知道ViewPager是罪魁禍首,因為當我隱藏它時,會調用onItemClick,所以這就是我想要的:

我創建了一個ViewGroup作為行(RowView)。 此ViewGroup具有onInterceptTouchEvent覆蓋,以避免ViewPager在檢測到單擊時處理更多觸摸事件。 但仍未調用onItemClick回調。 並且列表選擇器不會在單擊時顯示。 我想要這兩個功能。

這是RowView的onInterceptTouchEvent的樣子:

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    int actionMasked = ev.getActionMasked();
    switch(actionMasked) {
        case MotionEvent.ACTION_DOWN:
            Log.d("RowView", "OnInterceptTouchEvent - Down");
            tapDetector.onTouchEvent(ev);
            return false;
        case MotionEvent.ACTION_CANCEL:
            Log.d("RowView", "OnInterceptTouchEvent - Cancel");
            tapDetector.onTouchEvent(ev);
            break;
        case MotionEvent.ACTION_UP:
            if(tapDetector.onTouchEvent(ev)) {
                Log.d("RowView", "OnInterceptTouchEvent - UP!");
                return true;
            }
            break;
    }

    return super.onInterceptTouchEvent(ev);
}

有什么建議可以解決嗎?

編輯 :關於如何在ViewPager處於活動狀態時未調用MainActivity中的onItemClick的示例(Lollipop列表選擇器也未出現)

主要活動

ListView listView = (ListView) findViewById(R.id.main_list);
listView.setAdapter(new MainListAdapter(this, 30));
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        Log.d(TAG, "onItemClick: " + position);
    }
});

列表項XML:

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="80dp"
    android:descendantFocusability="blocksDescendants"
    >

    <TextView
        android:id="@+id/row_num"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentEnd="true"
        />

    <android.support.v4.view.ViewPager
        android:id="@+id/row_viewpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="visible"
        />
</RelativeLayout>

列表適配器:

public class MainListAdapter extends BaseAdapter {
    private Context context;
    private LayoutInflater inflater;
    private int count;

    public MainListAdapter(Context context, int count) {
        this.context = context;
        this.inflater = LayoutInflater.from(context);
        this.count = count;
    }

    @Override
    public int getCount() {
        return count;
    }

    @Override
    public Object getItem(int position) {
        return position;
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;

        if(convertView == null) {
            holder = new ViewHolder();
            convertView = createRow(parent, holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        holder.textView.setText(Integer.toString(position));
        int randomPages = (int) (new Random().nextDouble()*5+2);
        holder.viewPager.setAdapter(new NumAdapter(context, randomPages));

        return convertView;
    }

    private View createRow(ViewGroup parent, ViewHolder holder) {
        View view = inflater.inflate(R.layout.row_main_listview, parent, false);
        holder.textView = (TextView) view.findViewById(R.id.row_num);
        holder.viewPager = (ViewPager) view.findViewById(R.id.row_viewpager);
        view.setTag(holder);

        return view;
    }

    private static class ViewHolder {
        public TextView textView;
        public ViewPager viewPager;
    }
}

ViewPager的適配器:

public class NumAdapter extends PagerAdapter {
    private LayoutInflater inflater;
    private int count;

    public NumAdapter(Context context, int count) {
        this.inflater = LayoutInflater.from(context);
        this.count = count;
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        TextView textView = (TextView) inflater.inflate(R.layout.page_viewpager, container, false);
        textView.setText("Page " + position);
        container.addView(textView);
        return textView;
    }

    @Override
    public int getCount() {
        return count;
    }

    @Override
    public boolean isViewFromObject(View arg0, Object arg1) {
        return arg0 == arg1;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        container.removeView((View)object);
    }
}

我認為更好的覆蓋列表視圖onintercepter而不是viewgroup。

TouchEvent Flow簡單地說:

活動觸摸事件 - > viewgroup.dispatchtouchevent - > viewgroup.intercepter ..-> view.dispatchtouch ... - > .....

在這種情況下list.dispatch調用。 將事件ViewPager.dispatch 但在ViewPager.dispatchtouchevent之前,調用ListView.intercepterTouchEvent

如果dispatchTouchEvent返回false調用parent ViewTouchEvent但返回true call flow descent。

如果intercepterTouchEvent返回true則不調用子dispatchTouchEvent但返回false調用子dispatchTouchEvent 所以listview.intercepterTouchEvent返回true ,調用o nItemClick

所以如果listView.intercepterTouchEvent返回true ,則不刷過viewPager項。

您可以了解用戶的操作滑動或單擊2方式。 TouchEventguesturedetector ..

在listview的IntercepterTouchEvent(Event ev);

VelocityTracker mVelocityTracker;
PointF mLastPoint;
public mListView(Context context) {
    super(context);
    init();
}

public mListView(Context context, AttributeSet attrs) {
    super(context, attrs);
    init();
}

public mListView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init();
}

private void init(){
    mLastPoint = new PointF();
}


@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {

    if(mVelocityTracker == null)
        mVelocityTracker = VelocityTracker.obtain();
    mVelocityTracker.addMovement(ev);

    switch (ev.getAction()){
        case MotionEvent.ACTION_MOVE:
            mVelocityTracker.computeCurrentVelocity(100);
            int x = (int)Math.abs(mVelocityTracker.getXVelocity());
            int move_x = (int)Math.abs(ev.getX() - mLastPoint.x);
            Log.d("ListView","speed : " + x +" move_x : " + move_x);
            //here x is drag speed. (pixel/s)
            // change value left right both value you want speed and move amount
            if(move_x < 100 || x <100) {
                mLastPoint.set(ev.getX(), ev.getY());
                return true;
            }
            break;
        case MotionEvent.ACTION_DOWN:
            mLastPoint.set(ev.getX(), ev.getY());
            break;
        case MotionEvent.ACTION_UP:

            mVelocityTracker.recycle();mVelocityTracker = null;

            break;
    }
    return super.onInterceptTouchEvent(ev);
}

你可以刷速度約100或移動量100像素。 如果沒有執行點擊事件。

我希望這篇文章可以幫助你......

並添加編輯一些代碼打擊。

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {

    if(mVelocityTracker == null)
        mVelocityTracker = VelocityTracker.obtain();
    mVelocityTracker.addMovement(ev);

    switch (ev.getAction()){
        case MotionEvent.ACTION_MOVE:
            mVelocityTracker.computeCurrentVelocity(10);
            int x = (int)Math.abs(mVelocityTracker.getXVelocity());
            int move_x = (int)Math.abs(ev.getX() - mLastPoint.x);
            int move_y = (int)Math.abs(ev.getY() - mLastPoint.y);
            Log.d("ListView","speed : " + x +" move_x : " + move_x + " move_y : "+ move_y);
            if(move_x < move_y || x < 10) {
                mLastPoint.set(ev.getX(), ev.getY());
                return true;
            }else if(move_x > move_y){
                return false;
            }
            break;
        case MotionEvent.ACTION_DOWN:
            mLastPoint.set(ev.getX(), ev.getY());
            break;
        case MotionEvent.ACTION_UP:
            break;
    }
    return super.onInterceptTouchEvent(ev);
}

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    Log.d("ListView", "dispatch");
    switch (ev.getAction()){
        case MotionEvent.ACTION_MOVE:
            break;
        case MotionEvent.ACTION_DOWN:
            break;
        case MotionEvent.ACTION_UP:
            if(mVelocityTracker != null){mVelocityTracker.recycle();mVelocityTracker = null;}
            break;
    }
    return super.dispatchTouchEvent(ev);;
}

由於您的視圖是listview的孩子,它會消耗觸摸事件。 你可以通過讓你的觀看者孩子不可點擊來防止這種情況,即

    TextView textView =(TextView)inflater.inflate(R.layout.page_viewpager,container, false);

    textView.setText("Page " + position); 

    textView.setClickable(false);

我建議在每個ViewPager實例本身上設置OnClickListener ,並避免使用ListView的onItemClickListener 然后,您也可以完全刪除onInterceptTouchEvent() 那將是最簡單和穩定的解決方案。 更少的代碼 - 更少的錯誤;)

您需要執行以下操作之一:

Viewpager的子setOnClickListener上設置setOnClickListener或查找

android:descendantFocusability="beforeDescendants"

我希望這可以幫到你。

我認為這應該可以解決問題。

viewPager.getParent().requestDisallowInterceptTouchEvent(false);

我希望這能幫到您。

我們的想法是在視圖尋呼機而不是listView上進行點擊監聽

在getView方法中添加viewPager時,將其標記設置為行的位置

holder.viewPager.setTag(position);

然后將click監聽器添加到viewPager(也在getView中)

viewPager.setOnClickListener(new View.OnClickListener() {
    public void onClick(View v) {
        int position = v.getTag();
        Log.d(TAG, "onItemClick: " + position);

        //Fire a delegate or notification of whatever you want to do on the item click. You now have the position
        myClickListener.onItemClicked(position);

    }
});

問題出在列表適配器視圖中。 當我嘗試實現相同的功能(listview在listview行中)時,我遇到了類似的問題。 我通過這樣做解決了----在viewholder對象上設置onclicklistener而不是直接在listview上設置。 為此,您必須在適配器類上實現onitemclicklistener。

對阿德里安答案的改進:

public class CustomListView extends ListView {

    private GestureDetector gestureDetector;

    public CustomListView(Context context) {
        super(context);
        gestureDetector = new GestureDetector(context, new CustomListViewOnGestureListener());
    }

    public CustomListView(Context context, AttributeSet attrs) {
        super(context, attrs);
        gestureDetector = new GestureDetector(context, new CustomListViewOnGestureListener());
    }

    public CustomListView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        gestureDetector = new GestureDetector(context, new CustomListViewOnGestureListener());
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public CustomListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        gestureDetector = new GestureDetector(context, new CustomListViewOnGestureListener());
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        gestureDetector.onTouchEvent(ev);
        return super.dispatchTouchEvent(ev);
    }

    private class CustomListViewOnGestureListener extends GestureDetector.SimpleOnGestureListener {
        @Override
        public boolean onSingleTapUp(MotionEvent ev) {
            int firstVisiblePosition = getFirstVisiblePosition();
            int itemPosition = pointToPosition((int) ev.getX(), (int) ev.getY());
            int childIndex = itemPosition - firstVisiblePosition;
            View view = getChildAt(childIndex);
            performItemClick(view, itemPosition, getItemIdAtPosition(itemPosition));
            return true;
        }
    }
}

雖然列表選擇器不起作用,但這是我最終要做的事情。 這可以改進,但是,就目前而言,它是我喜歡它的唯一工作方式。

public class CustomListView extends ListView implements AbsListView.OnScrollListener {
    /*
     *  Used for detect taps
     */
    private GestureDetector tapDetector;
    /*
     *  As we need to set our own OnScrollListener, this stores the one 
     *  used outside, if any
     */
    private OnScrollListener onScrollListener;

    private boolean isScrolling = false;

    public CustomListView(Context context) {
        super(context);
        initView(context);
    }

    public CustomListView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView(context);
    }

    public CustomListView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView(context);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public CustomListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        initView(context);
    }

    private void initView(Context context) {
        tapDetector = new GestureDetector(context, new TapListener());
        super.setOnScrollListener(this);
    }

    @Override
    public void setOnScrollListener(OnScrollListener onScrollListener) {
        this.onScrollListener = onScrollListener;
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        boolean isTap = tapDetector.onTouchEvent(ev);

        if(ev.getActionMasked() == MotionEvent.ACTION_UP) {
            // Don't perform the click if the ListView is scrolling
            // so it is able to stop the scroll
            if(isTap && !isScrolling && hasOnItemClickListener()) {
                int itemPosition = pointToPosition((int)ev.getX(), (int)ev.getY());
                performItemClick(this, itemPosition, getItemIdAtPosition(itemPosition));
            }
        }

        return super.dispatchTouchEvent(ev);
    }

    public boolean hasOnItemClickListener() {
        return getOnItemClickListener() != null;
    }

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        isScrolling = scrollState != OnScrollListener.SCROLL_STATE_IDLE;

        if(this.onScrollListener != null) {
            onScrollListener.onScrollStateChanged(view, scrollState);
        }
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        if(this.onScrollListener != null) {
            onScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
        }
    }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM