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

本文转载自:http://blog.csdn.net/binbinqq86/article/details/46010951

项目用到了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>
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值