ListView satırındaki ViewPager onItemClick'in kovulmasını önler

ListView'ın her satırında ViewPager var. İyi çalışır, kullanıcı kaydırma hareketini kullandığında içindeki görünümleri değiştirir, ancak ListView'ın onItemClick yönteminin çağrılmasını engeller. ViewPager'in suçlu olduğunu biliyorum çünkü sakladığımda onItemClick çağrılıyor, yani deniyorum:

Bir satır görünümü (RowView) olmak için bir ViewGroup oluşturdum. Bu ViewGroup, bir tıklama tespit ettiğimde ViewPager'ın daha fazla dokunma olayıyla başa çıkmasını önlemek için onInterceptTouchEvent geçersiz kılındı. Ancak onItemClick geri çağrısı hala aranmıyor. Liste seçici de tıklamada gösterilmez. Bu iki özelliğin çalışmasını istiyorum.

RowView'dan onInterceptTouchEvent şöyle görünür:

@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);
}

Bunu çözmek için herhangi bir öneriniz var mı?

EDIT: Not working example about how the onItemClick in MainActivity is not called when the ViewPager is active (Lollipop list selector doesn't appear either)

MainActivity

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);
    }
});

Liste öğesi XML’si:



    

    

Liste bağdaştırıcısı:

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 Bağdaştırıcısı:

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);
    }
}
6
@Adrian: Yardım etmek isterdim, ama kahretsin, kayboldum .. ViewPager'ınızda ImageView'leri okuduğunuzu okudum ama sağladığınız snipper'da TextView'i görüyorum .. :)
katma yazar simekadam, kaynak
ViewPager , sayfa başına yalnızca bir ImageView gösteriyor mu yoksa düzende başka görünümler var mı?
katma yazar Paul Burke, kaynak
ListView 'iniz hakkında daha fazla ayrıntı gönderir misiniz?
katma yazar SilentKnight, kaynak
Görüşmeyi ViewPager'dan kesebilir ve işleminizi yapabilir misiniz?
katma yazar jyoon, kaynak
Görüntüleyicinizin içindeki resim ne durumda?
katma yazar jyoon, kaynak
@ simekadam Eh, çünkü koyduğum kod yapmaya çalıştığım şeyin bir örneği. Sorun ViewPager'dir ve içine TextView'leri veya ImageView'leri koyarsanız aynıdır.
katma yazar Adrian, kaynak
@jyoon ViewPager'ın içinde mi demek istiyorsunuz? ListView tıklama hakkında farketmezse, seçici gösterilmez. İşte sorun bu. ViewPager'ın içine çizilebilir bir dalgalanma koyarsam, ListView satırının başka bir öğesinin altında olacak, ancak sanırım istediğim gibi çalışamazsam, ViewPager içindeki ListView davranışını simüle etmeye çalışacağım. Teşekkürler!
katma yazar Adrian, kaynak
@jyoon Evet, tıklatma dinleyicileri koyabilirsiniz, ancak, ListView ile nasıl iletişim kurabilirim ki, dalgalanmalar ListView seçicisinde belirir ve onItemClickListener ateşlenir? Bunu denedim ama beklendiği gibi çalışmıyor.
katma yazar Adrian, kaynak
@jyoon Üzgünüm, ama bu kapsülleme ilkesini bozuyor. Bunun yanında, ViewPager'da bir onClick'i kaydedemezsiniz çünkü çalışmaz (sanırım sahip olduğum konuyla ilgili). Yine de teşekkürler.
katma yazar Adrian, kaynak

9 cevap

Ben oncepter listview viewgroup daha iyi geçersiz kılmak düşünüyorum.

Sadece TouchEvent Flow:

Acitivity touch event - > viewgroup.dispatchtouchevent -> viewgroup.intercepter..-> view.dispatchtouch... -> .....

bu durumda list.dispatch çağrısı. olayı ViewPager.dispatch 'e atın. ancak ViewPager.dispatchtouchevent 'den önce ListView.intercepterTouchEvent ' ı çağırın.

dispatchTouchEvent , false ana öğeyi görüntüle 'nin TouchEvent işlevini döndürür ancak true çağrı akışını döndür iniş.

intercepterTouchEvent , true ifadesini döndürürse dispatchTouchEvent çocuğunu çağırmadıysanız ancak false arayarak dispatchTouchEvent . bu nedenle listview.intercepterTouchEvent true değerini döndürerek o nItemClick öğesini çağırın.

bu nedenle, listView.intercepterTouchEvent true değerini döndürürse, viewPager öğelerini kaydırmayın.

Kullanıcının işlemlerini hızlıca öğrenebilir veya 2 yönlü tıklayabilirsiniz. TouchEvent ve guesturedetector ..

in listview's 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);
}

hızı 100'e kadar kaydırabilir veya 100 piksellik bir miktar taşıyabilirsiniz. click olayını gerçekleştirmediyse.

umarım bu yazı size yardımcı olabilir ......

ve bazı kod üfleme düzenlemelerini ekleyin.

@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);;
}
5
katma
Bu çözüm mükemmel thatnks @ 이남웅
katma yazar war_Hero, kaynak
oh ........ dispatchTouchEvent öğesinden önce kesişme elde et ve dispatchTouchEvent öğesinin false döndürürse, görünümün ana kesişme dokunma olayı. gerçek iniş olayı çocuğunu döndür ve çocuk dispatchTouchEvent. Tabii ki ACTION_UP başaramadı. Hassas dokunuş yapmak istiyorsanız, dispatchTouchEvent öğesini geçersiz kılın. şimdi evde değilim Eve gidiyorum bu konuda emin olun.
katma yazar 이남웅, kaynak
şimdi ne olduğunu anladım. açıklamaya çalışıyorum ama anlayabileceğini bilmiyorum. ACTION_UP ne zaman ulaşırsanız tıklayın. parmağınızın hareket edemediğini hissedebilirsiniz. ve elbette ACTION_UP IntercepterTouchEvent'te gerçekleşmez. çünkü ACTION_MOVE, onTouchEvent ve kaydırma yöntemine erişir. detay bilmiyorum ve Bir kod düzenleyin ve bir kod ekleyin. bu basit, mükemmel değil. ACTION_UP yaparken bazılarını gerçekleştirmek istiyorsanız dispatchTouch komutunu kullanın.
katma yazar 이남웅, kaynak
dokunma duyumunda, önce Etkinlik dispatchTouchEvent adı verilen ve daha sonra kök Mizanpajın (A) dispatchTouchEvent olarak adlandırılan bazı eylemleri algılar. Kök Düzen'in çocukları varsa, çocuklar (B) olarak adlandırılır dispatchTouhEvent . B çocuğu yoksa, A intercepterTouchEvent 'ı arayın. ancak çocuğunuz varsa, B'nin çocuğunu arayın dispatchEvent . Yine de, A'nın IntercepterTouchEvent adı verilen true değerini döndürün, A'nın onTouchEvent değerini elde edin ve A'nın onClickperform adını verin. ancak 'false' döndür, B onTouhEvent . dispatchTouchEvent , false değerini döndürürse, bu görünümün Dokunma Etkinliğini ve Tüm alçalmalar altını TouchEvent engeller.
katma yazar 이남웅, kaynak
Umudun için teşekkür ederim.
katma yazar 이남웅, kaynak
Önerilerinize cevap veriyorum. Teşekkürler!
katma yazar Adrian, kaynak
ViewPager'ı kaydırdığınızda tıklamanın tetiklenmemesini sağladım, ancak şu andaki tek sorun, kullanıcı parmağını biraz hareket ettirmedikçe tıklamanın tetiklenmemesi. Kullanıcı parmağını hareket ettirmiyorsa, ACTION_MOVE atılmaz ve ACTION_UP olayında, dokunma olayını engellemek için herhangi bir kod bulunmadığından, tıklatma yapılmaz. ACTION_MOVE işlevini ACTION_UP içinde işleyen aynı kodu koydum ve doğru olsa da hiçbir şey olmuyor.
katma yazar Adrian, kaynak
Çözümünüzü deniyorum ve kısmen çalışıyor gibi görünen tek kişi bu. Çözümünüzdeki sorun, sadece parmak hareket ederse işe yaramasıdır. Hareket etmiyorsa, tıklama ateşlenmez. Bunun yanı sıra, parmağınızı çok fazla hareket ettirseniz bile, tıklama hala gerçekleştiriliyor ve bence tıklamaların çalışma şekli bu değil. İstediğinizi elde etmek için çözümünüzü değiştirmeye çalıştım, ancak başarılı olamadım. ACTION_MOVE altındaki kodu ACTION_UP'a taşımayı denedim, ancak doğru olsa bile hiçbir şey olmuyor. Sanırım bunun nedeni UP'a dokunmadan dokunmak zorunda kalması. Herhangi bir fikir? Teşekkürler!
katma yazar Adrian, kaynak

Görüntüleme kitabınız listview'in çocuğu olduğu için, dokunma olaylarını tüketiyor. Bunu, görüntüleyenlerinizin çocuğunu anlaşılmaz hale getirerek önleyebilirsiniz, yani.

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

    textView.setText("Page " + position); 

    textView.setClickable(false);
3
katma
Bunu denedim, ama işe yaramadı. Yine de teşekkürler.
katma yazar Adrian, kaynak

Her ViewPager örneğinin her birine OnClickListener ayarını yapmayı ve ListView'ın onItemClickListener kullanımını engellemenizi öneririm. Daha sonra onInterceptTouchEvent() 'yı tamamen kaldırabilirsiniz. Bu en basit ve istikrarlı bir çözüm olurdu. Daha az kod - daha az hata;)

3
katma
Bu yaklaşım herkes için çalışıyor mu
katma yazar war_Hero, kaynak

Fikir, listView'da değil çağrı cihazında tıklama dinleyicisini yapmak

ViewPager'ı getView yöntemine eklerken, etiketini satırın konumuna getirin

holder.viewPager.setTag(position);

Sonra tıklama dinleyicisini viewPager'a ekleyin (ayrıca getView'da

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);

    }
});
2
katma

Aşağıdakilerden birini yapmanız gerekir:

Viewpager 'nin çocuğuna bir setOnClickListener ayarlayın veya bulun

android:descendantFocusability="beforeDescendants"

Umarım bu size yardımcı olabilir.

2
katma
afedersiniz. ancak bu soru sahibi bu soruyu düzenler. bu cevap şimdi düzenlenmeden önce cevaplandı. lütfen yukarıdaki cevabı okuyunuz. soruyu düzenledikten sonra tekrar cevap veriyorum.
katma yazar 이남웅, kaynak
eğer çok iyi ingilizce bilirsem .. bunu açıklarım ... üzgünüm
katma yazar 이남웅, kaynak
daha sonra görüntüleme cihazında dispatchtouchevent veya interceptertetouchevent'i değiştirin.
katma yazar 이남웅, kaynak
Bu cevap, sorduğu kişiye nasıl yardımcı olur?
katma yazar dev.mi, kaynak
Sorumu onInterceptTouchEvent geçersiz kılınmış olarak düzenledim. Bunu gözden geçirir misin?
katma yazar Adrian, kaynak
Bunu yapmaya çalıştım ama korkarım bu yöntemlerin nasıl çalıştığını anlamadım. Ben sadece ViewPager tokatlamak veya ListView onItemClick üzerinde çalıştım, ama hiçbiri ikisini de yapamadım.
katma yazar Adrian, kaynak
Ben zaten android denedim: descendantFocusability = "blockDescendants", ama işe yaramadı. Sanırım ViewPager dokunma olaylarını odağı almamış olsa bile ele aldığından dolayı. ViewPager çocuklarında setOnClickListener'ı ayarlama hakkında, ListView'in onItemClick'i oradan nasıl tetikleyebilirim? Olması gerektiği gibi çalışmasını tercih ederim, bu şekilde Lollipop'taki seçmen dalgalanmalarla çalışacaktı. Cevabınız için teşekkürler!
katma yazar Adrian, kaynak

Sorun listedeki adaptörlerin görüntüleyicisiyle ilgili. Aynı özelliği uygulamaya çalışırken benzer bir sorun yaşadım (bir liste görünümü satırındaki görüntüleyici). Bunu yaparak çözdüm ---- Doğrudan listeye ayarlamak yerine onclicklistener'ı görüntüleme nesnesine yerleştirin. Bunu yapmak için adaptör sınıfınıza onitemclicklistener uygulamanız gerekir.

2
katma

Bunun hile yapması gerektiğini düşünüyorum.

viewPager.getParent().requestDisallowInterceptTouchEvent(false);

Umarım bu sana yardımcı olmuştur.

2
katma
ViewPager'ı bağdaştırıcıda başlatırken bunu denedim, ancak işe yaramadı. Cevabınız için teşekkürler.
katma yazar Adrian, kaynak

Adrian'ın cevabında bir gelişme:

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;
        }
    }
}
1
katma

Liste seçici çalışmasa da, nihayet bunu yönetmek için yaptım. Bu iyileştirilebilir, ancak şimdilik, çalışma şeklini sevdiğim tek geçici çözüm bu.

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);
        }
    }
0
katma