zoukankan      html  css  js  c++  java
  • 自定义控件学习——仿qq侧滑栏

    效果

    主要步骤:

    1. 在xml布局里摆放内容. include
        2. 在自定义ViewGroup里, 进行measure测量, layout布局
        3. 响应用户的触摸事件
        4. int scrollX = (int) (downX - moveX);
        5. getScrollX()获取当前滚动到的位置
        6. 平滑动画

    先看布局

    layout_left

    <?xml version="1.0" encoding="utf-8"?>
    <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
                android:layout_width="240dp"
                android:layout_height="match_parent">
    
        <LinearLayout
            android:layout_width="240dp"
            android:layout_height="match_parent"
            android:orientation="vertical"
            android:background="@drawable/menu_bg">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="8dp"
            android:background="@drawable/selector_bg"
           android:drawablePadding="10dp"
            android:gravity="center_vertical"
            android:text="新闻"
            android:clickable="true"
            android:textColor="#ADCFD6"
            android:drawableLeft="@drawable/tab_news"
            android:textSize="18sp"/>
    
        </LinearLayout>
    </ScrollView>

    layout_content

    <?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:background="#FFFFFF"
        >
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:background="@drawable/top_bar_bg">
            <ImageButton
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@drawable/main_back"
                android:background="@null"/>
            <View
                android:layout_width="1dp"
                android:layout_height="match_parent"
                android:background="@drawable/top_bar_divider"/>
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="每日新闻"
                android:textSize="25sp"
                android:layout_gravity="center"/>
    
        </LinearLayout>
    
    </LinearLayout>

    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.xw.mystudeydemo.MainActivity">
    
    <com.example.mystudydemo.SlideMeun
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <include layout="@layout/layout_left"/>
        <include layout="@layout/layoit_content"/>
    </com.example.mystudydemo.SlideMeun>
    </RelativeLayout>

    布局中需要注意的是,layout_content中我们把整体布局背景设置成了android:background="#FFFFFF",这是因为我们需要他完全遮住下面的那层layout_left.xml

    layout_left的状态选择器

    <?xml version="1.0" encoding="utf-8"?>
    <selector xmlns:android="http://schemas.android.com/apk/res/android">
        <item android:state_pressed="true" android:drawable="@color/bg_pressed"/>
        <item android:drawable="@android:color/transparent"/>
    </selector>

    二,写自定义控件

    package com.example.mystudydemo;
    import android.content.Context;
    import android.util.AttributeSet;
    import android.view.MotionEvent;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.Scroller;
    
    /**
     * Created by xw on 2016/8/6.
     */
    public class SlideMeun  extends ViewGroup{
        private float downX;
        private float moveX;
        private Scroller scroller;
        /**
     * 侧滑面板控件, 抽屉面板.
     * @author poplar
     * 
     *   测量             摆放     绘制
      measure   ->  layout  ->  draw
          |           |          |
      onMeasure -> onLayout -> onDraw 重写这些方法, 实现自定义控件
      
          View流程
          onMeasure() (在这个方法里指定自己的宽高) -> onDraw() (绘制自己的内容)
          
          ViewGroup流程
          onMeasure() (指定自己的宽高, 所有子View的宽高)-> onLayout() (摆放所有子View) -> onDraw() (绘制内容)
     *
     */
    
        public SlideMeun(Context context) {
            super(context);
            init(context);
        }
    
        public SlideMeun(Context context, AttributeSet attrs) {
            super(context, attrs);
            init(context);
        }
    
       
    
        public SlideMeun(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init(context);
        }
         private void init(Context context) {
             // 初始化滚动器
            scroller=new Scroller(context);
            }
       
    
        /**
         * 测量并设置 所有子View的宽高
         * widthMeasureSpec: 当前控件的宽度测量规则
         * heightMeasureSpec: 当前控件的高度测量规则
         */
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
           // 指定左面板的宽高
            View leftMenu=getChildAt(0);
            leftMenu.measure(leftMenu.getLayoutParams().width,heightMeasureSpec);
                // 指定主面板的宽高
            View mainMenu=getChildAt(1);
            mainMenu.measure(mainMenu.getLayoutParams().width,heightMeasureSpec);
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    
        @Override
       
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
          // 摆放内容, 左面板,隐藏
            View leftMenu=getChildAt(0);
            leftMenu.layout(-leftMenu.getMeasuredWidth(),0,0,b);
    
            // 主面板
            View mainMenu=getChildAt(1);
            mainMenu.layout(l,t,r,b);
    
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            switch (event.getAction()){
                case MotionEvent.ACTION_DOWN:
                    downX = event.getX();
                    break;
                case MotionEvent.ACTION_MOVE:
                  moveX =event.getX();
                  
                       // 将要发生的偏移量/变化量
                   int scrollX=(int)(downX-moveX);
                   // 计算将要滚动到的位置, 判断是否会超出去, 超出去了.不执行scrollBy
                    // getScrollX() 当前滚动到的位置
                   int newposition=getScrollX()+scrollX;
                   if(newposition<-getChildAt(0).getMeasuredWidth()){   // 限定左边界
                       scrollTo(-getChildAt(0).getMeasuredWidth(), 0);
                   }
                   else if(newposition>0){    // 限定右边界
                       scrollTo(0, 0);
                   }
                   else{
                       // 让变化量生效
                   scrollBy(scrollX,0);
                   }
                    downX=moveX;
                    break;
                case MotionEvent.ACTION_UP:
                
                    int leftcenter=-(int)(getChildAt(0).getMeasuredWidth()/2.0f);
                    int startX=getScrollX();
                    int dx=0;
                        // 根据当前滚动到的位置, 和左面板的一半进行比较
                    if(startX<leftcenter){
                        // 打开, 切换成菜单面板
                        dx=-getChildAt(0).getMeasuredWidth()-startX;
                     
                        
                        
                    }
                    else{
                        // 关闭, 切换成主面板
                         dx=0-startX;
                         
                    }
                    int duration=Math.abs(dx*10);
                    scroller.startScroll(startX, 0, dx, 0, duration);
                    invalidate(); //重绘界面 -> drawChild() -> computeScroll();
                    break;
                    
                    
                default:
                    break;
            }
    
            return true;//消费事件
        }
        
        @Override  ////2. 维持动画的继续
        public void computeScroll() {
            
            super.computeScroll();
            if(scroller.computeScrollOffset()){
                int currX=scroller.getCurrX();
                scrollTo(currX, 0);
                invalidate();
            }
        }
    }

    三, MainActivity

    package com.example.mystudydemo;
    
    import android.app.Activity;
    import android.os.Bundle;
    import android.view.Window;
    import android.widget.Button;
    import android.widget.TextView;
    
    
    public class MainActivity extends Activity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            requestWindowFeature(Window.FEATURE_NO_TITLE);
            setContentView(R.layout.activity_main);
           
            
        }
    
      
    }
  • 相关阅读:
    linux学习之uniq
    hive学习05 参数设置
    【python】调用sm.ms图床api接口,实现上传图片并返回url
    【python】列表与数组之间的相互转换
    更新yum源
    要把RAID5创建卷组
    named-checkconf -z /etc/named.conf
    function_exists
    trigger_error
    命名空间
  • 原文地址:https://www.cnblogs.com/xurui1995/p/5745111.html
Copyright © 2011-2022 走看看