您的位置:首页 > 其它

listview侧滑菜单的实现——高仿QQ联系人列表

2015-05-26 17:45 483 查看
转载请注明出处:/article/1749210.html

项目用到了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>


以上就是整个项目的代码,不知道大家理解了没有,写的不好的地方,请大家多多包容与指正~~~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: