Android ListView实现上拉加载下拉刷新和滑动删除功能
最近项目需要用到可以滑动删除并且带有上拉加载下拉刷新的listview,查阅了一些资料,大多都是在swipemenulistview的基础上去添加头部和底部view,来扩展上拉加载和下拉刷新的功能,不过需要手动的去绘制ui及处理一些动画效果.用起来也不是特别方便.刚好项目中用到pulltorefreshlibrary库,就尝试着扩展了一个pulltorefreshswipemenulistview类来实现需求.先看一下效果:
实现步骤
一、组合pulltorefresh与swipemenulistview
pulltorefreshlibrary库中包含很多种可以上拉加载下拉刷新的控件,经常用到的比如pulltorefreshlistview,pulltorefreshgridview,pulltorefreshscrollview,它们的实现方式类似,本质上是在控件外部添加父布局,父布局中去添加控件的头部和底部view,来实现上拉加载下拉刷新的功能,所以扩展性很强.照猫画虎,copy一份pulltorefreshlistview,对他做一些处理.
protected listview createlistview(context context, attributeset attrs) { final listview lv; if (version.sdk_int >= version_codes.gingerbread) { lv = new internallistviewsdk9(context, attrs); } else { lv = new internallistview(context, attrs); } return lv; }
找到这部分代码,简单的做下修改:
protected swipemenulistview createlistview(context context, attributeset attrs) { final swipemenulistview lv; lv = new swipemenulistview(context, attrs); return lv; }
为什么要copy一份pulltorefreshlistview而不是其他的呢?因为swipemenulistview是继承自listview的(看名字就可以发现),修改起来比较方便.为什么要修改这部分代码呢?往下看.找一下引用这部分代码的地方:
@override protected listview createrefreshableview(context context, attributeset attrs) { listview lv = createlistview(context, attrs); // set it to this so it can be used in listactivity/listfragment lv.setid(android.r.id.list); return lv; }
是继承父类实现的方法,打开父类的该方法:
/** * this is implemented by derived classes to return the created view. if you * need to use a custom view (such as a custom listview), override this * method and return an instance of your custom class. * <p/> * be sure to set the id of the view in this method, especially if you're * using a listactivity or listfragment. * * @param context context to create view with * @param attrs attributeset from wrapped class. means that anything you * include in the xml layout declaration will be routed to the * created view * @return new instance of the refreshable view */ protected abstract t createrefreshableview(context context, attributeset attrs);
看一下注释,大概的意思是说,如果需要一个自定义的上拉加载下拉刷新view,可以返回该自定义view.并且返回值是t,一个泛型,说的很明白.看一下这个方法的引用mrefreshableview = createrefreshableview(context, attrs);看命名就知道,这个方法最终返回的是需要上拉加载下拉刷新功能的view.
感觉差不多可以了,来试一下,看一下效果:
好像效果不是很理想,有很多问题,比如滑动删除的时候可以上拉下拉,上拉时没有底部view.接下来来解决这些问题.
二、解决滑动冲突及相关问题
先来解决为什么没有底部view的问题,看一下copy过来修改过的pulltorefreshswipemenulistview类和pulltorefreshlistview类,对比一下到底有什么区别,使得pulltorefreshswipemenulistview没有底部view,在pulltorefreshswipemenulistview中只修改了一个部分,就是把之前返回的internallistview替换成了swipemenulistview,显然问题出现在internallistview上,查看一下internallistview(很容易找,是个内部类),可以看到这部分代码:
@override public void setadapter(listadapter adapter) { // add the footer view at the last possible moment if (null != mlvfooterloadingframe && !maddedlvfooter) { addfooterview(mlvfooterloadingframe, null, false); maddedlvfooter = true; } super.setadapter(adapter); }
底部view是在internallistview调用setadapter方法时添加上的,那头部view呢?查找一下,找到如下代码:
@override protected void handlestyledattributes(typedarray a) { super.handlestyledattributes(a); mlistviewextrasenabled = a.getboolean(com.handmark.pulltorefresh.library.r.styleable.pulltorefresh_ptrlistviewextrasenabled, true); if (mlistviewextrasenabled) { final framelayout.layoutparams lp = new framelayout.layoutparams(framelayout.layoutparams.match_parent, framelayout.layoutparams.wrap_content, gravity.center_horizontal); // create loading views ready for use later framelayout frame = new framelayout(getcontext()); mheaderloadingview = createloadinglayout(getcontext(), mode.pull_from_start, a); mheaderloadingview.setvisibility(view.gone); // framelayout添加头view frame.addview(mheaderloadingview, lp); // 添加头部view mrefreshableview.addheaderview(frame, null, false); mlvfooterloadingframe = new framelayout(getcontext()); mfooterloadingview = createloadinglayout(getcontext(), mode.pull_from_end, a); mfooterloadingview.setvisibility(view.gone); // framelayout添加底部view mlvfooterloadingframe.addview(mfooterloadingview, lp); // 添加底部view ... /** * if the value for scrolling while refreshing hasn't been * explicitly set via xml, enable scrolling while refreshing. */ if (!a.hasvalue(com.handmark.pulltorefresh.library.r.styleable.pulltorefresh_ptrscrollingwhilerefreshingenabled)) { setscrollingwhilerefreshingenabled(true); } } }
发现头部是在初始化的时候被添加上,而底部并没有添加(代码中…的部分),internallistview在setadapter方法中,添加上了底部,swipemenulistview中并没有相关的处理,所以在…处添加代码
mrefreshableview.addfooterview(mlvfooterloadingframe, null, false);把底部加上,之前发现的没有底部的问题就解决了(当然也可以仿照internallistview去修改一下swipemenulistview,不过比较麻烦,这里就不去做了)
接下来处理一下滑动删除时,可以进行上拉下拉操作的问题,很明显是滑动冲突的问题,想一下,当滑动删除时,不希望进行上拉下拉操作,而pulltorefreshswipemenulistview本身是一个viewgroup,所以当滑动删除时,屏蔽掉父布局的滑动事件就可以了,打开swipemenulistview,在滑动事件中做一下处理即可:
case motionevent.action_move: float dy = math.abs((ev.gety() - mdowny)); float dx = math.abs((ev.getx() - mdownx)); if (mtouchstate == touch_state_x) { if (mtouchview != null) { mtouchview.onswipe(ev); } getselector().setstate(new int[]{0}); ev.setaction(motionevent.action_cancel); super.ontouchevent(ev); // 处理滑动冲突,当处于滑动删除状态时,请求父布局不处理滑动事件 requestdisallowintercepttouchevent(true); return true; } else if (mtouchstate == touch_state_none) { if (math.abs(dy) > max_y) { mtouchstate = touch_state_y; } else if (dx > max_x) { mtouchstate = touch_state_x; if (monswipelistener != null) { monswipelistener.onswipestart(mtouchposition); } } } break;
很简单,就不多做说明了,不太清楚点击事件分发的童鞋,可以去查阅一下材料,这样一个带有上拉加载下拉刷新可滑动删除的listview就基本完工了.
三、完整代码
pulltorefreshswipemenulistview:
package swipemenulistview; import android.content.context; import android.content.res.typedarray; import android.util.attributeset; import android.view.gravity; import android.view.view; import android.widget.framelayout; import android.widget.listadapter; import android.widget.listview; import com.handmark.pulltorefresh.library.loadinglayoutproxy; import com.handmark.pulltorefresh.library.pulltorefreshadapterviewbase; import com.handmark.pulltorefresh.library.internal.loadinglayout; /** * created by junweiliu on 16/10/12. */ public class pulltorefreshswipemenulistview extends pulltorefreshadapterviewbase<listview> { private loadinglayout mheaderloadingview; private loadinglayout mfooterloadingview; private framelayout mlvfooterloadingframe; private boolean mlistviewextrasenabled; public pulltorefreshswipemenulistview(context context) { super(context); } public pulltorefreshswipemenulistview(context context, attributeset attrs) { super(context, attrs); } public pulltorefreshswipemenulistview(context context, mode mode) { super(context, mode); } public pulltorefreshswipemenulistview(context context, mode mode, animationstyle style) { super(context, mode, style); } @override public final orientation getpulltorefreshscrolldirection() { return orientation.vertical; } @override protected void onrefreshing(final boolean doscroll) { /** * if we're not showing the refreshing view, or the list is empty, the * the header/footer views won't show so we use the normal method. */ listadapter adapter = mrefreshableview.getadapter(); if (!mlistviewextrasenabled || !getshowviewwhilerefreshing() || null == adapter || adapter.isempty()) { super.onrefreshing(doscroll); return; } super.onrefreshing(false); final loadinglayout origloadingview, listviewloadingview, oppositelistviewloadingview; final int selection, scrolltoy; switch (getcurrentmode()) { case manual_refresh_only: case pull_from_end: origloadingview = getfooterlayout(); listviewloadingview = mfooterloadingview; oppositelistviewloadingview = mheaderloadingview; selection = mrefreshableview.getcount() - 1; scrolltoy = getscrolly() - getfootersize(); break; case pull_from_start: default: origloadingview = getheaderlayout(); listviewloadingview = mheaderloadingview; oppositelistviewloadingview = mfooterloadingview; selection = 0; scrolltoy = getscrolly() + getheadersize(); break; } // hide our original loading view origloadingview.reset(); origloadingview.hideallviews(); // make sure the opposite end is hidden too oppositelistviewloadingview.setvisibility(view.gone); // show the listview loading view and set it to refresh. listviewloadingview.setvisibility(view.visible); listviewloadingview.refreshing(); if (doscroll) { // we need to disable the automatic visibility changes for now disableloadinglayoutvisibilitychanges(); // we scroll slightly so that the listview's header/footer is at the // same y position as our normal header/footer setheaderscroll(scrolltoy); // make sure the listview is scrolled to show the loading // header/footer mrefreshableview.setselection(selection); // smooth scroll as normal smoothscrollto(0); } } @override protected void onreset() { /** * if the extras are not enabled, just call up to super and return. */ if (!mlistviewextrasenabled) { super.onreset(); return; } final loadinglayout originalloadinglayout, listviewloadinglayout; final int scrolltoheight, selection; final boolean scrolllvtoedge; switch (getcurrentmode()) { case manual_refresh_only: case pull_from_end: originalloadinglayout = getfooterlayout(); listviewloadinglayout = mfooterloadingview; selection = mrefreshableview.getcount() - 1; scrolltoheight = getfootersize(); scrolllvtoedge = math.abs(mrefreshableview.getlastvisibleposition() - selection) <= 1; break; case pull_from_start: default: originalloadinglayout = getheaderlayout(); listviewloadinglayout = mheaderloadingview; scrolltoheight = -getheadersize(); selection = 0; scrolllvtoedge = math.abs(mrefreshableview.getfirstvisibleposition() - selection) <= 1; break; } // if the listview header loading layout is showing, then we need to // flip so that the original one is showing instead if (listviewloadinglayout.getvisibility() == view.visible) { // set our original view to visible originalloadinglayout.showinvisibleviews(); // hide the listview header/footer listviewloadinglayout.setvisibility(view.gone); /** * scroll so the view is at the same y as the listview * header/footer, but only scroll if: we've pulled to refresh, it's * positioned correctly */ if (scrolllvtoedge && getstate() != state.manual_refreshing) { mrefreshableview.setselection(selection); setheaderscroll(scrolltoheight); } } // finally, call up to super super.onreset(); } @override protected loadinglayoutproxy createloadinglayoutproxy(final boolean includestart, final boolean includeend) { loadinglayoutproxy proxy = super.createloadinglayoutproxy(includestart, includeend); if (mlistviewextrasenabled) { final mode mode = getmode(); if (includestart && mode.showheaderloadinglayout()) { proxy.addlayout(mheaderloadingview); } if (includeend && mode.showfooterloadinglayout()) { proxy.addlayout(mfooterloadingview); } } return proxy; } protected swipemenulistview createlistview(context context, attributeset attrs) { final swipemenulistview lv; // if (version.sdk_int >= version_codes.gingerbread) { // lv = new internallistviewsdk9(context, attrs); // } else { // lv = new internallistview(context, attrs); // } lv = new swipemenulistview(context, attrs); return lv; } @override protected listview createrefreshableview(context context, attributeset attrs) { listview lv = createlistview(context, attrs); // set it to this so it can be used in listactivity/listfragment lv.setid(android.r.id.list); return lv; } @override protected void handlestyledattributes(typedarray a) { super.handlestyledattributes(a); mlistviewextrasenabled = a.getboolean(com.handmark.pulltorefresh.library.r.styleable.pulltorefresh_ptrlistviewextrasenabled, true); if (mlistviewextrasenabled) { final framelayout.layoutparams lp = new framelayout.layoutparams(framelayout.layoutparams.match_parent, framelayout.layoutparams.wrap_content, gravity.center_horizontal); // create loading views ready for use later framelayout frame = new framelayout(getcontext()); mheaderloadingview = createloadinglayout(getcontext(), mode.pull_from_start, a); mheaderloadingview.setvisibility(view.gone); frame.addview(mheaderloadingview, lp); mrefreshableview.addheaderview(frame, null, false); mlvfooterloadingframe = new framelayout(getcontext()); mfooterloadingview = createloadinglayout(getcontext(), mode.pull_from_end, a); mfooterloadingview.setvisibility(view.gone); mlvfooterloadingframe.addview(mfooterloadingview, lp); // 添加底部加载view mrefreshableview.addfooterview(mlvfooterloadingframe, null, false); /** * if the value for scrolling while refreshing hasn't been * explicitly set via xml, enable scrolling while refreshing. */ if (!a.hasvalue(com.handmark.pulltorefresh.library.r.styleable.pulltorefresh_ptrscrollingwhilerefreshingenabled)) { setscrollingwhilerefreshingenabled(true); } } } }
swipemenulistview:
package swipemenulistview; import android.content.context; import android.support.v4.view.motioneventcompat; import android.util.attributeset; import android.util.typedvalue; import android.view.motionevent; import android.view.view; import android.view.animation.interpolator; import android.widget.listadapter; import android.widget.listview; /** * @author baoyz * @date 2014-8-18 */ public class swipemenulistview extends listview { private static final int touch_state_none = 0; private static final int touch_state_x = 1; private static final int touch_state_y = 2; public static final int direction_left = 1; public static final int direction_right = -1; private int mdirection = 1;//swipe from right to left by default private int max_y = 5; private int max_x = 3; private float mdownx; private float mdowny; private int mtouchstate; private int mtouchposition; private swipemenulayout mtouchview; private onswipelistener monswipelistener; private swipemenucreator mmenucreator; private onmenuitemclicklistener monmenuitemclicklistener; private interpolator mcloseinterpolator; private interpolator mopeninterpolator; public swipemenulistview(context context) { super(context); init(); } public swipemenulistview(context context, attributeset attrs, int defstyle) { super(context, attrs, defstyle); init(); } public swipemenulistview(context context, attributeset attrs) { super(context, attrs); init(); } private void init() { max_x = dp2px(max_x); max_y = dp2px(max_y); mtouchstate = touch_state_none; } @override public void setadapter(listadapter adapter) { super.setadapter(new swipemenuadapter(getcontext(), adapter) { @override public void createmenu(swipemenu menu) { if (mmenucreator != null) { mmenucreator.create(menu); } } @override public void onitemclick(swipemenuview view, swipemenu menu, int index) { boolean flag = false; if (monmenuitemclicklistener != null) { flag = monmenuitemclicklistener.onmenuitemclick( view.getposition(), menu, index); } if (mtouchview != null && !flag) { mtouchview.smoothclosemenu(); } } }); } public void setcloseinterpolator(interpolator interpolator) { mcloseinterpolator = interpolator; } public void setopeninterpolator(interpolator interpolator) { mopeninterpolator = interpolator; } public interpolator getopeninterpolator() { return mopeninterpolator; } public interpolator getcloseinterpolator() { return mcloseinterpolator; } @override public boolean onintercepttouchevent(motionevent ev) { return super.onintercepttouchevent(ev); } @override public boolean ontouchevent(motionevent ev) { if (ev.getaction() != motionevent.action_down && mtouchview == null) return super.ontouchevent(ev); int action = motioneventcompat.getactionmasked(ev); action = ev.getaction(); switch (action) { case motionevent.action_down: int oldpos = mtouchposition; mdownx = ev.getx(); mdowny = ev.gety(); mtouchstate = touch_state_none; mtouchposition = pointtoposition((int) ev.getx(), (int) ev.gety()); if (mtouchposition == oldpos && mtouchview != null && mtouchview.isopen()) { mtouchstate = touch_state_x; mtouchview.onswipe(ev); return true; } view view = getchildat(mtouchposition - getfirstvisibleposition()); if (mtouchview != null && mtouchview.isopen()) { mtouchview.smoothclosemenu(); mtouchview = null; // return super.ontouchevent(ev); // try to cancel the touch event motionevent cancelevent = motionevent.obtain(ev); cancelevent.setaction(motionevent.action_cancel); ontouchevent(cancelevent); return true; } if (view instanceof swipemenulayout) { mtouchview = (swipemenulayout) view; mtouchview.setswipedirection(mdirection); } if (mtouchview != null) { mtouchview.onswipe(ev); } break; case motionevent.action_move: float dy = math.abs((ev.gety() - mdowny)); float dx = math.abs((ev.getx() - mdownx)); if (mtouchstate == touch_state_x) { if (mtouchview != null) { mtouchview.onswipe(ev); } getselector().setstate(new int[]{0}); ev.setaction(motionevent.action_cancel); super.ontouchevent(ev); // 处理滑动冲突,当处于滑动删除状态时,请求父布局不处理滑动事件 requestdisallowintercepttouchevent(true); return true; } else if (mtouchstate == touch_state_none) { if (math.abs(dy) > max_y) { mtouchstate = touch_state_y; } else if (dx > max_x) { mtouchstate = touch_state_x; if (monswipelistener != null) { monswipelistener.onswipestart(mtouchposition); } } } break; case motionevent.action_up: requestdisallowintercepttouchevent(false); if (mtouchstate == touch_state_x) { if (mtouchview != null) { mtouchview.onswipe(ev); if (!mtouchview.isopen()) { mtouchposition = -1; mtouchview = null; } } if (monswipelistener != null) { monswipelistener.onswipeend(mtouchposition); } ev.setaction(motionevent.action_cancel); super.ontouchevent(ev); return true; } break; } return super.ontouchevent(ev); } public void smoothopenmenu(int position) { if (position >= getfirstvisibleposition() && position <= getlastvisibleposition()) { view view = getchildat(position - getfirstvisibleposition()); if (view instanceof swipemenulayout) { mtouchposition = position; if (mtouchview != null && mtouchview.isopen()) { mtouchview.smoothclosemenu(); } mtouchview = (swipemenulayout) view; mtouchview.setswipedirection(mdirection); mtouchview.smoothopenmenu(); } } } private int dp2px(int dp) { return (int) typedvalue.applydimension(typedvalue.complex_unit_dip, dp, getcontext().getresources().getdisplaymetrics()); } public void setmenucreator(swipemenucreator menucreator) { this.mmenucreator = menucreator; } public void setonmenuitemclicklistener( onmenuitemclicklistener onmenuitemclicklistener) { this.monmenuitemclicklistener = onmenuitemclicklistener; } public void setonswipelistener(onswipelistener onswipelistener) { this.monswipelistener = onswipelistener; } public static interface onmenuitemclicklistener { boolean onmenuitemclick(int position, swipemenu menu, int index); } public static interface onswipelistener { void onswipestart(int position); void onswipeend(int position); } public void setswipedirection(int direction) { mdirection = direction; } }
mainactivity:
package com.example.junweiliu.pulltorefreshswipemenulistviewdemo; import android.graphics.color; import android.graphics.drawable.colordrawable; import android.os.bundle; import android.os.handler; import android.os.message; import android.support.v7.app.appcompatactivity; import android.util.typedvalue; import android.view.view; import android.widget.adapterview; import android.widget.listview; import android.widget.simpleadapter; import android.widget.toast; import com.handmark.pulltorefresh.library.pulltorefreshbase; import java.util.arraylist; import java.util.hashmap; import java.util.list; import java.util.map; import swipemenulistview.pulltorefreshswipemenulistview; import swipemenulistview.swipemenu; import swipemenulistview.swipemenucreator; import swipemenulistview.swipemenuitem; import swipemenulistview.swipemenulistview; public class mainactivity extends appcompatactivity { /** * 控件 */ private pulltorefreshswipemenulistview mpulltorefreshswipemenulistview; /** * 适配器 */ private simpleadapter madapter; /** * 数据源 */ list<map<string, object>> datas = new arraylist<map<string, object>>(); /** * 信息 */ private string[] message = {"数据0", "数据1", "数据2", "数据3", "数据4", "数据5", "数据6", "数据7", "数据8", "数据9", "数据10"}; @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_main); initdata(); initview(); } /** * 初始化控件 */ private void initview() { mpulltorefreshswipemenulistview = (pulltorefreshswipemenulistview) findviewbyid(r.id.psl_demo); madapter = new simpleadapter(this, datas, r.layout.item_adapter, new string[]{"message"}, new int[]{r.id.tv_message}); mpulltorefreshswipemenulistview.setmode(pulltorefreshbase.mode.both); mpulltorefreshswipemenulistview.setadapter(madapter); // 创建删除滑块 swipemenucreator creator = new swipemenucreator() { @override public void create(swipemenu menu) { swipemenuitem deleteitem = new swipemenuitem( getapplicationcontext()); deleteitem.setbackground(new colordrawable(color.rgb(0xff, 0x20, 0x20))); deleteitem.setwidth(dp2px(63)); // deleteitem.seticon(r.drawable.ic_delete); deleteitem.settitle("删除"); deleteitem.settitlesize(14); deleteitem.settitlecolor(color.white); menu.addmenuitem(deleteitem); } }; // 设置滑块 ((swipemenulistview) mpulltorefreshswipemenulistview.getrefreshableview()).setmenucreator(creator); // 滑块点击事件 ((swipemenulistview) mpulltorefreshswipemenulistview.getrefreshableview()).setonmenuitemclicklistener(new swipemenulistview.onmenuitemclicklistener() { @override public boolean onmenuitemclick(int position, swipemenu menu, int index) { switch (index) { case 0: toast.maketext(mainactivity.this, "删除我了" + position, toast.length_short).show(); break; } return false; } }); // 滑动监听 ((swipemenulistview) mpulltorefreshswipemenulistview.getrefreshableview()).setonswipelistener(new swipemenulistview.onswipelistener() { @override public void onswipestart(int position) { } @override public void onswipeend(int position) { } }); // 刷新加载事件 mpulltorefreshswipemenulistview.setonrefreshlistener(new pulltorefreshbase.onrefreshlistener2<listview>() { @override public void onpulldowntorefresh(pulltorefreshbase<listview> refreshview) { new handler(new handler.callback() { @override public boolean handlemessage(message message) { toast.maketext(mainactivity.this, "刷新成功", toast.length_short).show(); mpulltorefreshswipemenulistview.onrefreshcomplete(); return false; } }).sendemptymessagedelayed(1, 1000); } @override public void onpulluptorefresh(pulltorefreshbase<listview> refreshview) { new handler(new handler.callback() { @override public boolean handlemessage(message message) { toast.maketext(mainactivity.this, "加载成功", toast.length_short).show(); mpulltorefreshswipemenulistview.onrefreshcomplete(); return false; } }).sendemptymessagedelayed(2, 1000); } }); // 点击事件 mpulltorefreshswipemenulistview.getrefreshableview().setonitemclicklistener(new adapterview.onitemclicklistener() { @override public void onitemclick(adapterview<?> adapterview, view view, int i, long l) { } }); } /** * 初始化数据 */ private void initdata() { for (int i = 0; i < message.length; i++) { map<string, object> data = new hashmap<string, object>(); data.put("message", message[i]); datas.add(data); } } /** * dp转px * * @param dp * @return */ private int dp2px(int dp) { return (int) typedvalue.applydimension(typedvalue.complex_unit_dip, dp, getresources().getdisplaymetrics()); } }
activity_main:
<?xml version="1.0" encoding="utf-8"?> <relativelayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.junweiliu.pulltorefreshswipemenulistviewdemo.mainactivity"> <!--上拉加载下拉刷新滑动删除控件--> <swipemenulistview.pulltorefreshswipemenulistview android:id="@+id/psl_demo" android:layout_width="match_parent" android:layout_height="match_parent" > </swipemenulistview.pulltorefreshswipemenulistview> </relativelayout>
item_adapter:
<?xml version="1.0" encoding="utf-8"?> <linearlayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="vertical"> <textview android:id="@+id/tv_message" android:layout_width="wrap_content" android:layout_height="match_parent" android:paddingbottom="30dp" android:paddingtop="30dp" android:text="数据" android:textcolor="@android:color/black" android:textsize="16sp"/> </linearlayout>
本文已经被整理到《android下拉刷新上拉加载效果》,欢迎大家学习研究。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
上一篇: Android程序开发之获取汉字的首字母
下一篇: Android签名知识小结
推荐阅读
-
Android ListView实现上拉加载下拉刷新和滑动删除功能
-
android使用PullToRefresh实现下拉刷新和上拉加载
-
Android中Listview下拉刷新和上拉加载更多的多种实现方案
-
Android ListView实现上拉加载下拉刷新和滑动删除功能
-
Android中使用RecyclerView实现下拉刷新和上拉加载
-
android使用PullToRefresh实现下拉刷新和上拉加载
-
Android中Listview下拉刷新和上拉加载更多的多种实现方案
-
Android实现上拉加载更多以及下拉刷新功能(ListView)
-
Android下拉刷新上拉加载更多左滑动删除
-
Android实现上拉加载更多以及下拉刷新功能(ListView)