Practice, first put, demo at the end
Think that when Bo master just contact Android, see this effect in the heart only worship Ah, if slowly their own level also came up, put an idea of the year to a complete it.
Well, don't say much nonsense, first summarize this effect:
- The first is the need to customize the ListView, which is required, and then the event is processed in the ListView Ontouchevent method
- Ordinary item words, there is no way to achieve such a side-slip, even if you plug a horizontalscrollview into all can not, so also must customize a Itemview realize left and right slide
- Since the layout_width of the ListView is not necessarily match_parent, it may be a definite value such as 300DP, this time we need to establish a mechanism to ensure that the width of the itemview and the width of the ListView match, After all, Itemview contains two view, one is the body of the Contentview, one is the menu Menuview.
First I start with customizing the ListView, the ListView needs to do two things: event distribution and height matching. First look at the height match:
@Override protected void onmeasure (int widthmeasurespec, int heightmeasurespec) { int width = Measurespec.getsize (WIDTHMEASURESPEC); Width adaptation, change itemview width slideitemview.width = width; for (int i = 0; i < Getchildcount (); i++) { Slideitemview item = (Slideitemview) getchildat (i); Item.resetwidth (); } Super.onmeasure (Widthmeasurespec, Heightmeasurespec); }
This method is not difficult, gets the width of the ListView, and resets all the widths of the Itemview in memory. This step is very necessary, also said above, because you do not know the actual width of the ListView, then also talk about what to swipe left and right. The Resetwidth method of Slideitemview We put in the back to explain. Here's a little bit about it.Then there is the event distribution of the ListView, which is more important here:
@Override public boolean ontouchevent (motionevent ev) {float dx = 0; float dy = 0; Switch (ev.getaction ()) {Case MotionEvent.ACTION_DOWN:mTouchX = Ev.getx (); Mtouchy = Ev.gety (); Mmovex = Ev.getx (); Mmovey = Ev.gety (); Mtouchposition = pointtoposition ((int) ev.getx (), (int) ev.gety ()); Break Case MOTIONEVENT.ACTION_MOVE:DX = Ev.getx ()-Mmovex; DY = ev.gety ()-Mmovey; if (math.abs (dx) > Math.Abs (dy)) {//Get index value according to coordinate point int position = pointtoposition (int ) Ev.getx (), (int) ev.gety ()); if (mtouchposition! = listview.invalid_position && POSITION = = mtouchposition) {//Get real it in memory EM slideitemview Itemview = (slideitemview) getchildat (Position-getfirstvisibleposition ()); Itemview.Scroll ((int) dx); }} Mmovex = Ev.getx (); Mmovey = Ev.gety (); Break Case MOTIONEVENT.ACTION_UP:DX = Ev.getx ()-Mtouchx; DY = ev.gety ()-mtouchy; if (math.abs (dx) > Math.Abs (DY) && math.abs (dx) >= mtouchslop) {int position = Pointtoposi tion (int) ev.getx (), (int) ev.gety ()); if (mtouchposition! = listview.invalid_position && POSITION = = mtouchposition) {//Get real in-memory I TEM slideitemview Itemview = (slideitemview) getchildat (Position-getfirstvisibleposition ()); Determines whether the body content is displayed if (itemview.shouldshowcontent ((int) dx)) According to the current scrollx and DX { Itemview.showcontent (); }else{Itemview.showmenu (); }}else IF (Position! = mtouchposition) {Slideitemview Itemview = (slideitemview) getchildat (Mtouchposition- Getfirstvisibleposition ()); Depending on the current scrollx and DX to determine if the body content is displayed if (itemview.shouldshowcontent ((int) dx)) {i Temview.showcontent (); }else{Itemview.showmenu (); }}}else{Slideitemview Itemview = (slideitemview) getchildat (MTouch Position-getfirstvisibleposition ()); Determines whether the body content is displayed if (itemview.shouldshowcontent ((int) dx)) {Itemview, based on the current SCROLLX and DX. Showcontent (); }else{Itemview.showmenu (); }} break; Case MotionEvent.ACTION_CANCEL:if (mtouchposition! = listview.invalid_position) {SlideitEmview Itemview = (slideitemview) getchildat (Mtouchposition-getfirstvisibleposition ()); Itemview.showcontent (); } break; } return super.ontouchevent (EV); }
First of all, in the action_down to achieve the coordinates of the record, here need to record two sets of coordinate points, set is the coordinates of the down time, set is the coordinates of the move, the coordinates of the move when the initialization is necessary in Action_move. The position index of the ListView is then derived based on the currently pressed point.
It is also very important here, do not habitually return true in Action_down, if this returns True, then long indicates that the ListView will consume this event, and subsequent move events and up events are only passed to the ListView and not distributed to the child item, then the child item cannot be clicked. Please refer to "Android event distribution" for details. Then the action_move, if the absolute value of the x-coordinate when sliding is greater than the value of the y-coordinate, do the next step. The position index value of the ListView corresponding to the current sliding coordinates is drawn, and if the down-fit position index value is equal to the position index value of the move, the swipe starts.
here is an important concept is Getchildat (Position-getfirstvisibleposition ()), we know that the ListView is a cache mechanism, in memory can not exist GetCount () With so many numbers of view present, memory is stored only from getfirstvisiableposition () to Getlastvisiableposition () so many item in memory, So if we want the item in the memory of the current position, we need position-getfirstvisibleposition () to get the real index. Please refer to "Cache Policy for ListView" for details. Then it starts to slide, the slide is encapsulated in the Itemview operation, here is the main understanding on the line.And then action_up, where the absolute value of the x-coordinate is much more unexpected than that of the y-coordinate, and
An additional condition is that the absolute value of the x-coordinate is greater than a threshold, and only if it is greater than the threshold, is it important for us to think of it as sliding, which is the condition of the difference between a click operation or a sliding operation.。 After satisfying the condition, we make a judgment on the current DX offset value, if we need to show the text content, we will show the text, if we need to display the menu, the menu will be displayed. Currently, this judgment operation and display operation are encapsulated in the Itemview. If the current position and Action_down when the position is different, we believe that the item has been marked at this time, then we need to action_down the corresponding itemview to conduct a judgment and display operation. If the conditions of the sliding operation are not satisfied, we assume that at this point the ListView is scrolled up and down, and the corresponding Itemview of the Action_down event is also judged and displayed.Finally, Action_cancel, writing this event is mainly to prevent the sliding process of the event is interrupted after the slide into the half of the card there. The processing method is consistent with the above.
The next step is to show the Itemview part. This section requires a necessary knowledge point is Scroller, if not please refer to "Android Scroller", assuming you have learned about Scroller. Then you can then look down. First, you'll show the section setting body Content and Menu menus:
public void Setview (Slidelistview listView, int contentId, int menuId, float menuscale) { This.listview = Listview;
this.content = View.inflate (GetContext (), contentId, null); This.menu = View.inflate (GetContext (), menuId, null); This.scale = Menuscale; Layoutparams param1 = new Layoutparams (Width, layoutparams.match_parent); AddView (content, param1); Layoutparams param2 = new Layoutparams ((int) (Width * menuscale), layoutparams.match_parent); AddView (menu, param2); } Public View getcontent () { return content; } Public View GetMenu () { return menu; }
Very simple, very simple, just the corresponding LayoutID instantiation, and then AddView only, the only thing to be aware of is the width of layoutparams here, it is a static variable, its value is the external ListView width value. Next, let's look at the code for sliding:
public void Showcontent () { mscroller.startscroll (Mscroller.getfinalx (), Mscroller.getfinaly (),- Mscroller.getfinalx (), 0); Invalidate (); } public void ShowMenu () { mscroller.startscroll (Mscroller.getfinalx (), Mscroller.getfinaly (), Menu.getwidth ()- Mscroller.getfinalx (), 0); Invalidate (); } public boolean shouldshowcontent (int dx) { //Initialize if (menu.getwidth () = = 0) { resetwidth (); } if (dx > 0) { //right slide, when sliding over 1/4 start change if (Mscroller.getfinalx () < Menu.getwidth () * 3/4) { return true;< c17/>}else{ return false; } } else{ //left slide, when sliding over 1/4 start change if (Mscroller.getfinalx () < Menu.getwidth ()/4) { return true; } else{ return false;}} }
First look at the Shouldshowcontent method, here is an initialization operation, we'll talk about it later. If the DX is greater than 0, then it is sliding to the right, then the value of SCROLLX is less than 3/4 of the menu width, that is, sliding more than 1/4, we think we need to display the content of the body, otherwise the menu is displayed. The same is the case for DX less than 0. Then there is the Showcontent and ShowMenu method, where the SCROLLX is scrolled directly to the beginning of the two, meaning that both methods are called in the Action_up method of the ListView. It is important to note that the invalidate () method must be called. Otherwise it may not refresh, don't ask me how I know. I spent one hours to know the reason.Then is the scroll method that the Action_move method in the ListView needs to call:
public void Scroll (int dx) {if (dx > 0) {//Right-slip if (mscroller.getfinalx () > 0) { if (dx > Mscroller.getfinalx ()) {Mscroller.startscroll (Mscroller.getfinalx (), mscroller.getfinaly (),-mscroller.getfinalx (), 0); }else{Mscroller.startscroll (Mscroller.getfinalx (), Mscroller.getfinaly (),-DX, 0); }}else{mscroller.setfinalx (0); } invalidate (); }else{//Zoli if (Mscroller.getfinalx () < Menu.getwidth ()) {if (Mscroller.getfinalx () -DX > Menu.getwidth ()) {Mscroller.startscroll (Mscroller.getfinalx (), Mscroller.getfinaly (), menu.ge Twidth ()-Mscroller.getfinalx (), 0); }else{Mscroller.startscroll (Mscroller.getfinalx (), Mscroller.getfinaly (),-DX, 0); }}else{Mscroller.setfinalx (Menu.getwidth ()); } invalidate (); } }
Here there is a right and left boundary value of the judgment problem, we look directly at the code bar. The words are not clear. Logic is not really difficult either.The last is the Resetwidth method:
/** * Resets the width, which is called in the Onmeasure method of the ListView. * This method is to dynamically adapt the width of the ListView, because the Layout_width of the ListView is not necessarily equal to match_parent * or it may be a fixed value such as 300DP */public void Resetwidth () { Viewgroup.layoutparams param1 = Content.getlayoutparams (); if (param1 = = null) { param1 = new Layoutparams (Width, layoutparams.match_parent); } else{ param1.width = width; } Content.setlayoutparams (param1); Viewgroup.layoutparams param2 = Menu.getlayoutparams (); if (param2 = = null) { param2 = new Layoutparams ((int) (Width * scale), layoutparams.match_parent); } else{ param2.width = (int) (width * scale); } Menu.setlayoutparams (param2); }
It's nothing, it's a matter of changing the width of the content and menu menus.
OK, the source explanation is complete, the following gives the test example:The first is activity_main.xml:
<?xml version= "1.0" encoding= "Utf-8"? ><relativelayout xmlns:android= "http://schemas.android.com/apk/res/ Android " android:layout_width=" match_parent " android:layout_height=" match_parent "> < Cc.wxf.slide.SlideListView android:id= "@+id/listview" android:layout_width= "Match_parent" android: layout_height= "Match_parent" android:cachecolorhint= "@android: color/transparent" android:listselector= "@android: Color/transparent" android:dividerheight= "1DP" android:divider= "@android: Color/darker_gray " /></relativelayout>
Then the mainactivity:
public class Mainactivity extends Activity { @Override public void OnCreate (Bundle savedinstancestate) { Super.oncreate (savedinstancestate); Setcontentview (r.layout.activity_main); Slidelistview ListView = (slidelistview) Findviewbyid (R.id.listview); Listview.setadapter (New Slideadapter (this, ListView));} }
Then there is the custom adapter, which needs a good look, especially the GetView and Viewholder handling:
public class Slideadapter extends Baseadapter {private context context; Private Slidelistview ListView; Public Slideadapter (context context, Slidelistview ListView) {this.context = context; This.listview = ListView; } private string[] data = new string[]{"1231231", "232131231", "1231231", "232131231", "1231231", "232131231", "1 231231 "," 232131231 "," 1231231 "," 232131231 "," 1231231 "," 232131231 "," 1231231 "," 232131231 "}; @Override public int GetCount () {return data.length; } @Override public Object getItem (int position) {return data[position]; } @Override public long getitemid (int position) {return position; } @Override public View getView (int position, view Convertview, ViewGroup parent) {Viewholder holder = null; if (Convertview = = null) {Slideitemview Itemview = new Slideitemview (context); Itemview.setview (ListView, R.layout.item_content, R.layout.item_menu, 2.0f/3); Holder = new Viewholder (Itemview); Itemview.settag (holder); Convertview = Itemview; }else{holder = (viewholder) convertview.gettag (); } holder.textView.setText (Data[position]); Final Slideitemview Itemview = (slideitemview) Convertview; Holder.imageView.setOnClickListener (New View.onclicklistener () {@Override public void OnClick (View V) {Toast.maketext (context, "clicked ImageView", Toast.length_short). Show (); Itemview.showcontent (); } }); Holder.textView.setOnClickListener (New View.onclicklistener () {@Override public void OnClick (View V {Toast.maketext (context, "clicked TextView", Toast.length_short). Show (); Itemview.showcontent (); } }); Holder.btn1.setOnClickListener (New View.onclicklistener () {@Override public void OnClick (VieW v) {toast.maketext (context, "clicked Btn1", Toast.length_short). Show (); Itemview.showcontent (); } }); Holder.btn2.setOnClickListener (New View.onclicklistener () {@Override public void OnClick (View v) { Toast.maketext (Context, "clicked Btn2", Toast.length_short). Show (); Itemview.showcontent (); } }); return convertview; } public class Viewholder {public ImageView ImageView; Public TextView TextView; Public TextView btn1; Public TextView btn2; Public Viewholder (Slideitemview view) {View content = View.getcontent (); ImageView = (ImageView) Content.findviewbyid (R.id.imageview); TextView = (TextView) Content.findviewbyid (R.id.textview); View menu = View.getmenu (); BTN1 = (TextView) Menu.findviewbyid (R.ID.BTN1); BTN2 = (TextView) Menu.findviewbyid (R.ID.BTN2); } }}
And finally, there are two layout files.Item_content.xml:
<?xml version= "1.0" encoding= "Utf-8"? ><relativelayout xmlns:android= "http://schemas.android.com/apk/res/ Android "Android:layout_width=" Match_parent "android:layout_height=" wrap_content "> <imageview an Droid:id= "@+id/imageview" android:layout_width= "wrap_content" android:layout_height= "Wrap_content" an droid:src= "@mipmap/ic_launcher" android:layout_centervertical= "true" android:layout_marginleft= "10DP" /> <textview android:id= "@+id/textview" android:layout_width= "Wrap_content" Android:layout_ height= "Wrap_content" android:text= "@string/app_name" android:textsize= "15sp" android:textcolor= "@and Roid:color/black "android:layout_centervertical=" true "android:layout_alignparentright=" true "Android : layout_marginright= "10DP"/></relativelayout>
Item_menu.xml:
<?xml version= "1.0" encoding= "Utf-8"? ><linearlayout xmlns:android= "http://schemas.android.com/apk/res/ Android "Android:layout_width=" Wrap_content "android:layout_height=" wrap_content "android:orientation=" Horizontal "android:gravity=" center "android:padding=" 20DP "android:background=" @android: Color/holo_red_light "> & Lt TextView android:id= "@+id/btn1" android:layout_width= "Match_parent" android:layout_weight= "1" android:layout_height= "match_parent" android:gravity= "center" android:text= "@string/btn1" Android:tex Tsize= "15SP" android:textcolor= "@android: Color/black"/> <textview android:id= "@+id/btn2" Android:layout_width= "Match_parent" android:layout_weight= "1" android:layout_height= "Match_parent" android:gravity= "center" android:text= "@string/btn2" android:textsize= "15sp" android:textcolor= "@a Ndroid:color/black "/></LinearLayout>
All right, all is done. Release Demo: Point me to download demo
Android Custom ListView Implementation side-Slip submenu