Saya memiliki ScrollView yang mengelilingi seluruh tata letak saya sehingga seluruh layar dapat digulir. Elemen pertama yang saya miliki dalam ScrollView ini adalah blok HorizontalScrollView yang memiliki fitur yang dapat digulir secara horizontal. Saya telah menambahkan daftar ontouchlist ke horizontalscrollview untuk menangani acara sentuh dan memaksa tampilan untuk "snap" ke gambar terdekat di acara ACTION_UP.
Jadi efek yang saya tuju adalah seperti homescreen android stok di mana Anda dapat menggulir dari satu ke yang lain dan terkunci ke satu layar ketika Anda mengangkat jari Anda.
Ini semua berfungsi dengan baik kecuali untuk satu masalah: Saya perlu menggesek dari kiri ke kanan hampir sempurna secara horizontal agar ACTION_UP pernah mendaftar. Jika saya menggesek paling tidak secara vertikal (yang saya pikir banyak orang cenderung lakukan di ponsel mereka ketika menggesek sisi ke sisi), saya akan menerima ACTION_CANCEL bukan ACTION_UP. Teori saya adalah ini karena horizontalscrollview berada dalam scrollview, dan scrollview membajak sentuhan vertikal untuk memungkinkan pengguliran vertikal.
Bagaimana saya bisa menonaktifkan acara sentuh untuk scrollview hanya dari dalam scrollview horizontal saya, tetapi masih memungkinkan untuk bergulir vertikal normal di tempat lain di scrollview?
Ini contoh kode saya:
public class HomeFeatureLayout extends HorizontalScrollView {
private ArrayList<ListItem> items = null;
private GestureDetector gestureDetector;
View.OnTouchListener gestureListener;
private static final int SWIPE_MIN_DISTANCE = 5;
private static final int SWIPE_THRESHOLD_VELOCITY = 300;
private int activeFeature = 0;
public HomeFeatureLayout(Context context, ArrayList<ListItem> items){
super(context);
setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
setFadingEdgeLength(0);
this.setHorizontalScrollBarEnabled(false);
this.setVerticalScrollBarEnabled(false);
LinearLayout internalWrapper = new LinearLayout(context);
internalWrapper.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
internalWrapper.setOrientation(LinearLayout.HORIZONTAL);
addView(internalWrapper);
this.items = items;
for(int i = 0; i< items.size();i++){
LinearLayout featureLayout = (LinearLayout) View.inflate(this.getContext(),R.layout.homefeature,null);
TextView header = (TextView) featureLayout.findViewById(R.id.featureheader);
ImageView image = (ImageView) featureLayout.findViewById(R.id.featureimage);
TextView title = (TextView) featureLayout.findViewById(R.id.featuretitle);
title.setTag(items.get(i).GetLinkURL());
TextView date = (TextView) featureLayout.findViewById(R.id.featuredate);
header.setText("FEATURED");
Image cachedImage = new Image(this.getContext(), items.get(i).GetImageURL());
image.setImageDrawable(cachedImage.getImage());
title.setText(items.get(i).GetTitle());
date.setText(items.get(i).GetDate());
internalWrapper.addView(featureLayout);
}
gestureDetector = new GestureDetector(new MyGestureDetector());
setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (gestureDetector.onTouchEvent(event)) {
return true;
}
else if(event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL ){
int scrollX = getScrollX();
int featureWidth = getMeasuredWidth();
activeFeature = ((scrollX + (featureWidth/2))/featureWidth);
int scrollTo = activeFeature*featureWidth;
smoothScrollTo(scrollTo, 0);
return true;
}
else{
return false;
}
}
});
}
class MyGestureDetector extends SimpleOnGestureListener {
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
try {
//right to left
if(e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
activeFeature = (activeFeature < (items.size() - 1))? activeFeature + 1:items.size() -1;
smoothScrollTo(activeFeature*getMeasuredWidth(), 0);
return true;
}
//left to right
else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
activeFeature = (activeFeature > 0)? activeFeature - 1:0;
smoothScrollTo(activeFeature*getMeasuredWidth(), 0);
return true;
}
} catch (Exception e) {
// nothing
}
return false;
}
}
}
MeetMe's HorizontalListView
perpustakaan.HomeFeatureLayout extends HorizontalScrollView
) di sini velir.com/blog/index.php/2010/11/17/... Ada beberapa komentar tambahan tentang apa yang terjadi ketika kelas gulir khusus dibuat.Jawaban:
Pembaruan: Saya menemukan jawabannya. Pada ScrollView saya, saya perlu mengganti metode onInterceptTouchEvent untuk hanya mencegat acara sentuh jika gerakan Y adalah> gerakan X. Sepertinya perilaku default ScrollView adalah mencegat acara sentuh setiap kali ada gerakan APAPUN Y. Jadi dengan perbaikannya, ScrollView hanya akan mencegat acara jika pengguna dengan sengaja menggulir ke arah Y dan dalam kasus itu memberikan ACTION_CANCEL kepada anak-anak.
Ini adalah kode untuk kelas Tampilan Gulir saya yang berisi HorizontalScrollView:
sumber
mGestureDetector.onTouchEvent(ev)
akan dipanggil. Seperti sekarang, itu tidak akan dipanggil jikasuper.onInterceptTouchEvent(ev)
itu salah. Saya baru saja menemukan sebuah kasus di mana anak-anak yang dapat diklik di layar gulir dapat mengambil acara sentuh dan onScroll tidak akan dipanggil sama sekali. Kalau tidak, terima kasih, jawaban yang bagus!Terima kasih Joel karena memberi saya petunjuk tentang cara mengatasi masalah ini.
Saya telah menyederhanakan kode (tanpa perlu GestureDetector ) untuk mencapai efek yang sama:
sumber
Saya pikir saya menemukan solusi yang lebih sederhana, hanya ini menggunakan subkelas dari ViewPager bukan (induknya) ScrollView.
UPDATE 2013-07-16 : Saya menambahkan override
onTouchEvent
juga. Mungkin bisa membantu dengan masalah yang disebutkan dalam komentar, meskipun YMMV.Ini mirip dengan teknik yang digunakan di android.widget.Gallery's onScroll () . Lebih lanjut dijelaskan oleh presentasi Google I / O 2013 Menulis Tampilan Kustom untuk Android .
Pembaruan 2013-12-10 : Pendekatan serupa juga dijelaskan dalam posting dari Kirill Grouchnikov tentang aplikasi (saat itu) Android Market .
sumber
ScrollView
denganLinearLayout
di manaUninterceptableViewPager
ditempatkan. Memang,ret
selalu salah ... Ada petunjuk bagaimana cara memperbaikinya?TableRow
dalamTableLayout
yang ada di dalamScrollView
(ya, saya tahu ...), dan berfungsi sebagaimana mestinya. Mungkin Anda bisa mencobaonScroll
mengganti alih-alihonInterceptTouchEvent
, seperti Google melakukannya (baris 1010)Saya telah menemukan bahwa kadang-kadang satu ScrollView mendapatkan kembali fokus dan yang lainnya kehilangan fokus. Anda dapat mencegahnya, dengan hanya memberikan salah satu fokus scrollView:
sumber
Itu tidak bekerja dengan baik untuk saya. Saya mengubahnya dan sekarang bekerja dengan lancar. Kalau ada yang tertarik.
sumber
Berkat Neevek, jawabannya bekerja untuk saya tetapi tidak mengunci pengguliran vertikal ketika pengguna mulai menggulir tampilan horizontal (ViewPager) ke arah horizontal dan kemudian tanpa mengangkat gulir jari secara vertikal, ia mulai menggulir tampilan wadah yang mendasarinya (ScrollView) . Saya memperbaikinya dengan membuat sedikit perubahan pada kode Neevak:
sumber
Ini akhirnya menjadi bagian dari dukungan perpustakaan v4, NestedScrollView . Jadi, tidak diperlukan lagi peretasan lokal untuk sebagian besar kasus.
sumber
Solusi Neevek bekerja lebih baik daripada Joel pada perangkat yang menjalankan versi 3.2 ke atas. Ada bug di Android yang akan menyebabkan java.lang.IllegalArgumentException: pointerIndex di luar jangkauan jika detektor gerakan digunakan di dalam scollview. Untuk menduplikasi masalah, terapkan scollview khusus seperti yang disarankan Joel dan letakkan pager tampilan di dalamnya. Jika Anda menyeret (jangan mengangkat sosok Anda) ke satu arah (kiri / kanan) lalu ke sebaliknya, Anda akan melihat tabrakan. Juga dalam solusi Joel, jika Anda menyeret pager tampilan dengan menggerakkan jari Anda secara diagonal, begitu jari Anda meninggalkan area tampilan konten pager tampilan, pager akan kembali ke posisi sebelumnya. Semua masalah ini lebih berkaitan dengan desain internal Android atau kurang dari itu daripada implementasi Joel, yang itu sendiri adalah bagian dari kode yang cerdas dan ringkas.
http://code.google.com/p/android/issues/detail?id=18990
sumber