listview侧滑菜单的实现——高仿QQ联系人列表
2016-06-06 19:59
477 查看
本文转载自:http://blog.csdn.net/binbinqq86/article/details/46010951
项目用到了ListView的侧滑删除的功能,由于当时项目比较赶,就随便在网上找了一个,但是效果不是太好,最近闲了下来,就想自己实现一个,于是就按照QQ的联系人列表的侧滑菜单做了一个,效果基本上是一模一样的。在这个过程中,自己也学习到了不少的东西,下面就把这个过程跟大家分享出来。
废话不多说,首先上效果图。
看完了图如果感觉效果不好,请不要拍砖,奋斗好的话请继续往下看~得意
下面结合代码说说实现的原理:首先自定义一个ViewGroup来实现item的滑动效果。
自定义的item就这么多,里面包含了添加自身布局及菜单布局,以及滑动菜单的处理,下面是ListView的重写,因为主要逻辑都在item中,所有重写的ListView就简单多了,废话不多说,请看代码
自定义的ListView主要就是做一个触摸事件的拦截及向子View(即item)的事件分发,这里首先讲一下Android触摸事件的分发机制。
一个最简单的屏幕触摸动作触发了一系列Touch事件:ACTION_DOWN->ACTION_MOVE->ACTION_MOVE->ACTION_MOVE…->ACTION_MOVE->ACTION_UP
Android系统中的每个View的子类都具有下面三个和TouchEvent处理密切相关的方法:
1)public boolean dispatchTouchEvent(MotionEvent ev) 这个方法用来分发TouchEvent
2)public boolean onInterceptTouchEvent(MotionEvent ev) 这个方法用来拦截TouchEvent
3)public boolean onTouchEvent(MotionEvent ev) 这个方法用来处理TouchEvent
当TouchEvent发生时,首先Activity将TouchEvent传递给最顶层的View,TouchEvent最先到达最顶层view的 dispatchTouchEvent,然后由dispatchTouchEvent方法进行分发,如果dispatchTouchEvent返回true或者false,事件均不会继续向下传递,如果down后返回false,则move和up都不会被接受,只能接受下个动作。这里为什么特别指定的down事件呢,因为如果down返回true,说明后续事件会被传递于此,但是move返回false呢?哈哈,这个就不会影响了,因此说down才是关键。此方法一般用于初步处理事件,因为动作是由此分发,所以通常会调用super.dispatchTouchEvent,这样就会继续调用onInterceptTouchEvent,再由onInterceptTouchEvent决定事件流向。如果 interceptTouchEvent 返回 true ,也就是拦截掉了,则交给它的 onTouchEvent 来处理,如果 interceptTouchEvent 返回 false ,那么就传递给子 view ,由子 view 的 dispatchTouchEvent 再来开始这个事件的分发。如果事件传递到某一层的子 view 的 onTouchEvent 上了,这个方法返回了 false ,那么这个事件会从这个 view 往上传递,都是 onTouchEvent 来接收。而如果传递到最上面的 onTouchEvent 也返回 false 的话,这个事件就会“消失”,而且接收不到下一次事件。
里面有个重要方法在此要特别说明一下:requestDisallowInterceptTouchEvent。当检测到水平有移动距离的时候,则调用此方法,将滑动事件交给子View来处理,而ListView自身不再进行垂直滑动,否则会出现水平跟垂直滑动有冲突。
以上两个自定义View都讲完了,下面就说说怎么用(终于派上用场了)
下面是用到的布局
slidmenuright.xml
slidmenuitem.xml
slid_layout_menu3.xml
项目用到了ListView的侧滑删除的功能,由于当时项目比较赶,就随便在网上找了一个,但是效果不是太好,最近闲了下来,就想自己实现一个,于是就按照QQ的联系人列表的侧滑菜单做了一个,效果基本上是一模一样的。在这个过程中,自己也学习到了不少的东西,下面就把这个过程跟大家分享出来。
废话不多说,首先上效果图。
看完了图如果感觉效果不好,请不要拍砖,奋斗好的话请继续往下看~得意
下面结合代码说说实现的原理:首先自定义一个ViewGroup来实现item的滑动效果。
package com.binbin.slid; import android.annotation.SuppressLint; import android.content.Context; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.widget.ListView; import android.widget.Scroller; /** * 此视图可以单独作为一个ListView的item * 可以侧滑拉出菜单的自定义View,如ListView侧滑删除效果 * 菜单item一起滑动 * @author tianbin * * Created on 2015-5-5 下午4:32:19 */ public class TSlidLayout extends ViewGroup{ /** 用于滑动的类*/ private Scroller mScroller; /** 用来跟踪触摸速度的类*/ private VelocityTracker mVelocityTracker; /** 最小滑动的速度*/ private static final int SNAP_VELOCITY = 300; /**最小滑动距离,超过了,才认为开始滑动 */ private int mTouchSlop = 0 ; /**上次触摸的X坐标*/ private float mLastX = -1; /**上次触摸的Y坐标*/ private float mLastY = -1; private Context mContext; /**菜单与item视图*/ private View menu,mContentView; /**viewgroup的宽高*/ private int maxWidth,maxHeight; /**滑出菜单是否可见*/ private boolean isMenuVisible=false; /**滑出菜单的宽度*/ private int slidMenuWidth=0; /**是否正在左右滑动*/ private boolean isSliding=false; /**为每个item记录位置,判断点击的是哪个item*/ public int pos=-1; public TSlidLayout(Context context) { super(context); // TODO Auto-generated constructor stub init(context); } public TSlidLayout(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub init(context); } @SuppressLint("NewApi") public TSlidLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); // TODO Auto-generated constructor stub init(context); } private void init(Context context) { this.mContext=context; mScroller = new Scroller(context); mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); slidMenuWidth=mContext.getResources().getDimensionPixelSize(R.dimen.slidemenu_right_width); } /** * 添加视图和滑出菜单 * @param content * @param menuLeft * @param menuRight */ public void addItemAndMenu(View content,View menu){ //具体宽高在addView的时候设置,里面的控件充满行高 addView(content,new LayoutParams(-1, -1)); addView(menu,new LayoutParams(mContext.getResources().getDimensionPixelSize(R.dimen.slidemenu_right_width),-1)); this.menu=menu; this.mContentView=content; content.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // 调用ListView的item点击事件 ((ListView)getParent()).performItemClick(v, pos, pos); } }); } /** * 计算所有ChildView的宽度和高度 然后根据ChildView的计算结果,设置ViewGroup自己的宽和高 * Exactly:width代表的是精确的尺寸 AT_MOST:width代表的是最大可获得的空间 MATCH_PARENT(FILL_PARENT)对应于EXACTLY,WRAP_CONTENT对应于AT_MOST 其他情况(有具体值的)也对应于EXACTLY */ @SuppressLint("NewApi") @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); //当我们需要重写onMeasure时,记得要调用setMeasuredDimension来设置自身的mMeasuredWidth和mMeasuredHeight,否则,就会抛出异常 /** * 获得此ViewGroup上级容器为其推荐的宽和高,以及计算模式 */ // final int widthMode = MeasureSpec.getMode(widthMeasureSpec); // final int heightMode = MeasureSpec.getMode(heightMeasureSpec); // // int measuredWidth = MeasureSpec.getSize(widthMeasureSpec); // int measuredHeight = MeasureSpec.getSize(heightMeasureSpec); /** * 根据childView计算的出的宽和高,计算容器的宽和高,主要用于容器是warp_content时 */ for (int i = 0,count = getChildCount(); i < count; i++) { View childView = getChildAt(i); //获取每个子view的自己高度宽度,取最大的就是viewGroup的大小 measureChild(childView, widthMeasureSpec, heightMeasureSpec); maxWidth = Math.max(maxWidth,childView.getMeasuredWidth()); maxHeight = Math.max(maxHeight,childView.getMeasuredHeight()); } //为ViewGroup设置宽高 setMeasuredDimension(maxWidth,maxHeight); // 计算出所有的childView的宽和高---可用 // measureChildren(widthMeasureSpec, heightMeasureSpec); /** * 设置所有的childView的宽和高,此处如果不设置,会造成多个子view的情况下,有的子view设置成match_parent但是不能充满父控件的问题 */ //首先判断params.width的值是多少,有三种情况。 //如果是大于零的话,及传递的就是一个具体的值,那么,构造MeasupreSpec的时候可以直接用EXACTLY。 //如果为-1的话,就是MatchParent的情况,那么,获得父View的宽度,再用EXACTLY来构造MeasureSpec。 //如果为-2的话,就是wrapContent的情况,那么,构造MeasureSpec的话直接用一个负数就可以了。 for (int i = 0,count = getChildCount(); i < count; i++) { View childView = getChildAt(i); int widthSpec = 0; int heightSpec = 0; LayoutParams params = childView.getLayoutParams(); if(params.width > 0){ widthSpec = MeasureSpec.makeMeasureSpec(params.width, MeasureSpec.EXACTLY); }else if (params.width == -1) { widthSpec = MeasureSpec.makeMeasureSpec(maxWidth, MeasureSpec.EXACTLY); } else if (params.width == -2) { widthSpec = MeasureSpec.makeMeasureSpec(maxWidth, MeasureSpec.AT_MOST); } if(params.height > 0){ heightSpec = MeasureSpec.makeMeasureSpec(params.height, MeasureSpec.EXACTLY); }else if (params.height == -1) { heightSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.EXACTLY); } else if (params.height == -2) { heightSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST); } childView.measure(widthSpec, heightSpec); } } /* * 首先执行onMeasure,然后就会执行onLayout * 为子View指定位置:相对父控件的位置!!!!!! */ @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { //此处left,top相对父视图为0,0 mContentView.layout(0, 0, right, maxHeight); menu.layout(mContentView.getMeasuredWidth(), 0, mContentView.getMeasuredWidth()+menu.getMeasuredWidth(), maxHeight); } /** * 注:Scroller中:正值代表向左移动,负值代表向右移动 */ @Override public boolean dispatchTouchEvent(MotionEvent ev) { // TODO Auto-generated method stub if (mVelocityTracker == null) { // 使用obtain方法得到VelocityTracker的一个对象 mVelocityTracker = VelocityTracker.obtain(); } mVelocityTracker.addMovement(ev); mVelocityTracker.computeCurrentVelocity(1000); // 获得当前的速度 int velocityX = (int) mVelocityTracker.getXVelocity(); switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: mLastX=ev.getRawX(); mLastY=ev.getRawY(); break; case MotionEvent.ACTION_MOVE: // 计算当前的速度 if(Math.abs(velocityX)>SNAP_VELOCITY||(Math.abs(mLastX-ev.getRawX())>mTouchSlop)){ isSliding=true; int deltaX = (int) (mLastX - ev.getRawX()); mLastX = ev.getRawX(); if(getScrollX()>=0&&getScrollX()<=slidMenuWidth){ if(isMenuVisible){ if(getScrollX()+deltaX<=0){ deltaX=-getScrollX(); } if((getScrollX()+deltaX)>=slidMenuWidth){ //此时菜单可见,不能左滑,只能右滑隐藏菜单 deltaX=0; } }else{ if((getScrollX()+deltaX)>=slidMenuWidth){ deltaX=slidMenuWidth-getScrollX(); } if(getScrollX()+deltaX<=0){ //菜单不可见,此时不能右滑,只能左滑显示菜单 deltaX=0; } } scrollBy(deltaX,0); } } break; default: isSliding=false; //速度加滑动距离满足一个即自动显示或隐藏 int delta=0; if(isMenuVisible){ //右菜单可见时 if(velocityX >= SNAP_VELOCITY||(slidMenuWidth-getScrollX())>=slidMenuWidth/3){ //自动隐藏 delta=-getScrollX(); mScroller.startScroll(getScrollX(), 0,delta, 0); invalidate(); } if((velocityX>0&&velocityX < SNAP_VELOCITY)||(slidMenuWidth-getScrollX())<slidMenuWidth/3){ //自动显示 delta=slidMenuWidth-getScrollX(); mScroller.startScroll(getScrollX(), 0,delta, 0); invalidate(); } }else{ //右菜单不可见时 if(velocityX <= -SNAP_VELOCITY||getScrollX()>=slidMenuWidth/3){ //滑动速度超过或者滑动距离超过一半时,松手自动显示 delta=slidMenuWidth-getScrollX(); mScroller.startScroll(getScrollX(), 0,delta,0); invalidate(); } if((velocityX<0&&velocityX > -SNAP_VELOCITY)||getScrollX()<slidMenuWidth/3){ //自动隐藏 delta=-getScrollX(); mScroller.startScroll(getScrollX(), 0,delta, 0); //startScroll只是设置滑动的初始化参数,一定要调用下面这句,才能真正开始滑动 invalidate(); } } if (mVelocityTracker != null) { mVelocityTracker.recycle(); mVelocityTracker = null; } break; } return super.dispatchTouchEvent(ev); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { // TODO Auto-generated method stub if(isSliding){ //如果正在滑动(非点击),则拦截此事件,不传递给子View return true; } return super.onInterceptTouchEvent(ev); } /** * ViewGroup在分发绘制自己的孩子的时候,会对其子View调用computeScroll()方法 */ @Override public void computeScroll() { // TODO Auto-generated method stub if (mScroller.computeScrollOffset()) { scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); postInvalidate(); }else{//滑动结束后,改变菜单状态 changeMenuVisibleState(); } } /** * 在手动或者自动滚动完成后,改变菜单可见状态 */ private void changeMenuVisibleState(){ //必须在滚动完成后,判断菜单是否可见,否则会出现判断错误的情况 if(getScrollX()==slidMenuWidth){ isMenuVisible=true; }else{ isMenuVisible=false; } } /*** * 当菜单可见时,隐藏它 */ public void hideMenuWithAnimation(){ mScroller.startScroll(getScrollX(), 0,-getScrollX(), 0); invalidate(); isMenuVisible=false; } /*** * 删除的时候,瞬间隐藏所有菜单 */ public void hideMenu() { //此函数参数意义:首先瞬间移动到startX,然后在规定的时间内缓慢平移指定距离(dy:非坐标) //一般startX用getScrollX()代表从当前位置开始平移 mScroller.startScroll(0, 0, 0, 0, 0); invalidate(); isMenuVisible=false; } public boolean getIsMenuVisible(){ return isMenuVisible; } }
自定义的item就这么多,里面包含了添加自身布局及菜单布局,以及滑动菜单的处理,下面是ListView的重写,因为主要逻辑都在item中,所有重写的ListView就简单多了,废话不多说,请看代码
package com.binbin.slid; import android.annotation.SuppressLint; import android.content.Context; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; import android.widget.ListView; public class TListView extends ListView { /**上次触摸的X坐标*/ private float mLastX = -1; private int mLastPointToPosition=-1; /**最小滑动距离,超过了,才认为开始滑动 */ private int mTouchSlop = 0 ; public TListView(Context context) { super(context); // TODO Auto-generated constructor stub init(context); } public TListView(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub init(context); } public TListView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); // TODO Auto-generated constructor stub init(context); } @SuppressLint("NewApi") public TListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); // TODO Auto-generated constructor stub init(context); } private void init(Context context) { mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { // TODO Auto-generated method stub switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: mLastX=ev.getRawX(); //在此处改变mLastPointToPosition,否则return时未能改变 int posTemp=mLastPointToPosition; mLastPointToPosition=pointToPosition((int)ev.getX(), (int)ev.getY()); //此处注意:在GridView和ListView中,getChildAt ( int position ) 方法中position指的是当前可见区域的第几个元素,而不是整个listview中的位置 for(int i=0;i<=getLastVisiblePosition()-getFirstVisiblePosition();i++){ if(getChildAt(i)!=null){ TSlidLayout tsl=(TSlidLayout)getChildAt(i); //当有菜单出现时,只能点击菜单,点击其他任何地方均收起菜单 if(tsl.getIsMenuVisible()){ if(posTemp!=(pointToPosition((int)ev.getX(), (int)ev.getY()))){ //说明点击的不是有菜单的那个item tsl.hideMenuWithAnimation(); //拦截此事件,不再向下传递 return false; }else{ //如果点击的不是菜单,则隐藏菜单,否则传递给子View if(inRangeOfView(tsl.getChildAt(0), ev)){ tsl.hideMenuWithAnimation(); //拦截此事件,不再向下传递,包括自身的事件传递 return false; } } } } } break; case MotionEvent.ACTION_MOVE: if((Math.abs(mLastX-ev.getRawX())>mTouchSlop)){ //只要水平方向有滑动,就不进行垂直滑动(请求不允许拦截子View触摸事件,即交给子View处理) //此时不会调用本身的onTouchEvent requestDisallowInterceptTouchEvent(true); } break; default: break; } return super.dispatchTouchEvent(ev); } /** * 判断是否点击在view的内部 * @param view * @param ev * @return * true 点击在view的内部 * false 点击在view的外部 */ private boolean inRangeOfView(View view, MotionEvent ev) { int[] location = new int[2]; //此处需要取在屏幕上的坐标,并且只需判断x坐标,因为listview中点击的item已经确定 view.getLocationOnScreen(location); int x = location[0]; if (ev.getX() < x || ev.getX() > (x + view.getWidth())) { return false; } return true; } public void hideAllMenuView(){ for(int i=0;i<=getLastVisiblePosition()-getFirstVisiblePosition();i++){ if(getChildAt(i)!=null){ TSlidLayout tsl=(TSlidLayout)getChildAt(i); //当有菜单出现时,只能点击菜单,点击其他任何地方均收起菜单 if(tsl.getIsMenuVisible()){ tsl.hideMenu(); } } } } }
自定义的ListView主要就是做一个触摸事件的拦截及向子View(即item)的事件分发,这里首先讲一下Android触摸事件的分发机制。
一个最简单的屏幕触摸动作触发了一系列Touch事件:ACTION_DOWN->ACTION_MOVE->ACTION_MOVE->ACTION_MOVE…->ACTION_MOVE->ACTION_UP
Android系统中的每个View的子类都具有下面三个和TouchEvent处理密切相关的方法:
1)public boolean dispatchTouchEvent(MotionEvent ev) 这个方法用来分发TouchEvent
2)public boolean onInterceptTouchEvent(MotionEvent ev) 这个方法用来拦截TouchEvent
3)public boolean onTouchEvent(MotionEvent ev) 这个方法用来处理TouchEvent
当TouchEvent发生时,首先Activity将TouchEvent传递给最顶层的View,TouchEvent最先到达最顶层view的 dispatchTouchEvent,然后由dispatchTouchEvent方法进行分发,如果dispatchTouchEvent返回true或者false,事件均不会继续向下传递,如果down后返回false,则move和up都不会被接受,只能接受下个动作。这里为什么特别指定的down事件呢,因为如果down返回true,说明后续事件会被传递于此,但是move返回false呢?哈哈,这个就不会影响了,因此说down才是关键。此方法一般用于初步处理事件,因为动作是由此分发,所以通常会调用super.dispatchTouchEvent,这样就会继续调用onInterceptTouchEvent,再由onInterceptTouchEvent决定事件流向。如果 interceptTouchEvent 返回 true ,也就是拦截掉了,则交给它的 onTouchEvent 来处理,如果 interceptTouchEvent 返回 false ,那么就传递给子 view ,由子 view 的 dispatchTouchEvent 再来开始这个事件的分发。如果事件传递到某一层的子 view 的 onTouchEvent 上了,这个方法返回了 false ,那么这个事件会从这个 view 往上传递,都是 onTouchEvent 来接收。而如果传递到最上面的 onTouchEvent 也返回 false 的话,这个事件就会“消失”,而且接收不到下一次事件。
里面有个重要方法在此要特别说明一下:requestDisallowInterceptTouchEvent。当检测到水平有移动距离的时候,则调用此方法,将滑动事件交给子View来处理,而ListView自身不再进行垂直滑动,否则会出现水平跟垂直滑动有冲突。
以上两个自定义View都讲完了,下面就说说怎么用(终于派上用场了)
package com.binbin.slid; import java.util.ArrayList; import java.util.List; import android.app.Activity; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.BaseAdapter; import android.widget.TextView; import android.widget.Toast; public class MainActivity extends Activity { private TListView lv; private List<String> str=new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); for(int i=0;i<20;i++){ str.add(i+"个"); } lv=(TListView) findViewById(R.id.lv); lv.setAdapter(new MyAdapter()); lv.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { // TODO Auto-generated method stub Toast.makeText(MainActivity.this, id+"aaaaaaaaaaaaaaaaa"+position, 0).show(); } }); } class MyAdapter extends BaseAdapter{ @Override public int getCount() { // TODO Auto-generated method stub return str.size(); } @Override public Object getItem(int position) { // TODO Auto-generated method stub return str.get(position); } @Override public long getItemId(int position) { // TODO Auto-generated method stub return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { // TODO Auto-generated method stub HolderView holder = null; if (convertView == null) { holder = new HolderView(); convertView=View.inflate(MainActivity.this,R.layout.slidmenuitem, null); holder.tsl=(TSlidLayout) convertView.findViewById(R.id.tsl); holder.content=View.inflate(MainActivity.this,R.layout.slid_layout_menu3, null); holder.menu=View.inflate(MainActivity.this,R.layout.slidmenuright, null); holder.tsl.addItemAndMenu(holder.content,holder.menu); convertView.setTag(holder); }else { holder = (HolderView) convertView.getTag(); } final int pos=position; //必须设置此句,否则不知道点击的是哪个item中的menu holder.tsl.pos=position; holder.menu.findViewById(R.id.menu_delete).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub str.remove(pos); notifyDataSetChanged(); lv.hideAllMenuView(); } }); holder.menu.findViewById(R.id.menu_hello).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub Toast.makeText(MainActivity.this, "hello"+pos, 0).show(); } }); ((TextView)holder.content.findViewById(R.id.tv_top)).setText(str.get(position)); return convertView; } } static class HolderView { public TSlidLayout tsl; public View content; public View menu; } }
下面是用到的布局
slidmenuright.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:gravity="center" android:orientation="horizontal" > <TextView android:id="@+id/menu_hello" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:background="@android:color/darker_gray" android:gravity="center" android:padding="20dp" android:text="HELLO" android:textColor="#fff" android:textSize="18sp" /> <TextView android:id="@+id/menu_delete" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:background="#aa2369" android:gravity="center" android:padding="20dp" android:text="删除" android:textColor="#fff" android:textSize="18sp" /> </LinearLayout>
slidmenuitem.xml
<com.binbin.slid.TSlidLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/tsl" android:descendantFocusability="blocksDescendants" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#fa2365" />
slid_layout_menu3.xml
<?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:orientation="horizontal" > <TextView android:id="@+id/tv_top" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#235690" android:gravity="center" android:text="item" android:textColor="#fff" android:textSize="20sp" /> </LinearLayout>
相关文章推荐
- 用x264和ffmpeg将YUV编码为.h264(1)
- 龙书D3D11章节习题答案(第九章)
- 华为的一道很简单的笔试题,当时答得很烂,现在拿出来重新搞了一下
- iOS开发(OC)——根据时间戳转换成不同的显示
- 华为机试题之二:输入一串用空格隔开的数字串,对于数字串的奇数位按升序排序,偶数位按降序排序.
- memcached全面剖析–3. memcached的删除机制和发展方向
- 一个多态性的游戏状态机系统
- Linux下Clion编译Cmake报错的解决方案
- 第十五周补充程序
- memcached全面剖析–2. 理解memcached的内存存储
- Android——Timer TimerTask定时任务实现App界面欢迎页 自动跳转
- 守护进程
- svn update 出现skipped '.' 或skipped '目录名称'
- ITIL发展的云计算之路
- CentOS 7 升级Python到3.5后,yum,和gnome-twear-tool 出现的问题
- 华为面试题之洞穴逃生
- phprpc简单使用
- Android SO逆向1-ARM介绍
- 删除链表的结点---o(1)
- memcached完全剖析–1. memcached的基础