实现这样的效果:
## 侧滑面板(对ViewGroup的自定义)
* 应用场景: 扩展主面板的功能
* 功能实现:
> 1. ViewDragHelper: Google2013年IO大会提出的,
>  解决界面控件拖拽移动问题. (v4包下)
> 2. mTouchSlop 最小敏感范围, 值越小, 越敏感

* 伴随动画:
> 1. 左面板: 缩放动画, 平移动画, 透明度动画
> 2. 主面板: 缩放动画
> 3. 背景动画: 亮度变化 (颜色变化)

* 状态监听\触摸优化:
> 1. 设置并更新状态
> 2. 触摸优化: 重写ViewGroup里onInterceptTouchEvent和onTouchEvent

新v4、看大小
nineoldandroids.jar  属性动画,兼容9个低版本
ActionBarSherlock 

 
 
布局:
  1.  

    <com.drag.DragLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/dl"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/bg"
    tools:context=".MainActivity" >
    <LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="50dp"
    android:paddingLeft="10dp"
    android:paddingRight="50dp"
    android:paddingTop="50dp" >
    <ImageView
    android:layout_width="50dp"
    android:layout_height="50dp"
    android:src="@drawable/head" />
    <ListView
    android:id="@+id/lv_left"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    </ListView>
    </LinearLayout>
    <com.drag.MyLinearLayout
    android:id="@+id/mll"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#ffffff"
    android:orientation="vertical" >
    <RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="50dp"
    android:background="#18B6EF"
    android:gravity="center_vertical" >
    <ImageView
    android:id="@+id/iv_header"
    android:layout_width="30dp"
    android:layout_height="30dp"
    android:layout_marginLeft="15dp"
    android:src="@drawable/head" />
    <TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerHorizontal="true"
    android:text="Header" />
    </RelativeLayout>
    <ListView
    android:id="@+id/lv_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    </ListView>
    </com.drag.MyLinearLayout>
    </com.drag.DragLayout>

      Utils:单例的toast

  2. public class Utils {
    public static Toast mToast;
    public static void showToast(Context mContext, String msg) {
    if (mToast == null) {
    mToast = Toast.makeText(mContext, "", Toast.LENGTH_SHORT);
    }
    mToast.setText(msg);
    mToast.show();
    } /**
    * dip 转换成 px
    * @param dip
    * @param context
    * @return
    */
    public static float dip2Dimension(float dip, Context context) {
    DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
    return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, displayMetrics);
    }
    /**
    * @param dip
    * @param context
    * @param complexUnit {@link TypedValue#COMPLEX_UNIT_DIP} {@link TypedValue#COMPLEX_UNIT_SP}}
    * @return
    */
    public static float toDimension(float dip, Context context, int complexUnit) {
    DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
    return TypedValue.applyDimension(complexUnit, dip, displayMetrics);
    }
    /** 获取状态栏高度
    * @param v
    * @return
    */
    public static int getStatusBarHeight(View v) {
    if (v == null) {
    return 0;
    }
    Rect frame = new Rect();
    v.getWindowVisibleDisplayFrame(frame);
    return frame.top;
    }
    public static String getActionName(MotionEvent event) {
    String action = "unknow";
    switch (MotionEventCompat.getActionMasked(event)) {
    case MotionEvent.ACTION_DOWN:
    action = "ACTION_DOWN";
    break;
    case MotionEvent.ACTION_MOVE:
    action = "ACTION_MOVE";
    break;
    case MotionEvent.ACTION_UP:
    action = "ACTION_UP";
    break;
    case MotionEvent.ACTION_CANCEL:
    action = "ACTION_CANCEL";
    break;
    case MotionEvent.ACTION_SCROLL:
    action = "ACTION_SCROLL";
    break;
    case MotionEvent.ACTION_OUTSIDE:
    action = "ACTION_SCROLL";
    break;
    default:
    break;
    }
    return action;
    }
    }

      DragLayout:

  3. /**
    * 侧滑面板
    * @author poplar
    *
    */
    public class DragLayout extends FrameLayout {
    private static final String TAG = "TAG";
    private ViewDragHelper mDragHelper;
    private ViewGroup mLeftContent;
    private ViewGroup mMainContent;
    private OnDragStatusChangeListener mListener;
    private Status mStatus = Status.Close; /**
    * 状态枚举
    */
    public static enum Status {
    Close, Open, Draging;
    }
    public interface OnDragStatusChangeListener{
    void onClose();
    void onOpen();
    void onDraging(float percent);
    } public Status getStatus() {
    return mStatus;
    }
    public void setStatus(Status mStatus) {
    this.mStatus = mStatus;
    }
    public void setDragStatusListener(OnDragStatusChangeListener mListener){
    this.mListener = mListener;
    } public DragLayout(Context context) {
    this(context, null);
    }
    public DragLayout(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
    }
    public DragLayout(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle); // a.初始化 (通过静态方法)
    mDragHelper = ViewDragHelper.create(this , mCallback); } ViewDragHelper.Callback mCallback = new ViewDragHelper.Callback() {
    // c. 重写事件 // 1. 根据返回结果决定当前child是否可以拖拽
    // child 当前被拖拽的View
    // pointerId 区分多点触摸的id
    @Override
    public boolean tryCaptureView(View child, int pointerId) {
    Log.d(TAG, "tryCaptureView: " + child);
    return true;
    }; @Override
    public void onViewCaptured(View capturedChild, int activePointerId) {
    Log.d(TAG, "onViewCaptured: " + capturedChild);
    // 当capturedChild被捕获时,调用.
    super.onViewCaptured(capturedChild, activePointerId);
    }
    @Override
    public int getViewHorizontalDragRange(View child) {
    // 返回拖拽的范围, 不对拖拽进行真正的限制. 仅仅决定了动画执行速度
    return mRange;
    } // 2. 根据建议值 修正将要移动到的(横向)位置 (重要)
    // 此时没有发生真正的移动
    public int clampViewPositionHorizontal(View child, int left, int dx) {
    // child: 当前拖拽的View
    // left 新的位置的建议值, dx 位置变化量
    // left = oldLeft + dx;
    Log.d(TAG, "clampViewPositionHorizontal: "
    + "oldLeft: " + child.getLeft() + " dx: " + dx + " left: " +left); if(child == mMainContent){
    left = fixLeft(left);
    }
    return left;
    }
    // 3. 当View位置改变的时候, 处理要做的事情 (更新状态, 伴随动画, 重绘界面)
    // 此时,View已经发生了位置的改变
    @Override
    public void onViewPositionChanged(View changedView, int left, int top,
    int dx, int dy) {
    // changedView 改变位置的View
    // left 新的左边值
    // dx 水平方向变化量
    super.onViewPositionChanged(changedView, left, top, dx, dy);
    Log.d(TAG, "onViewPositionChanged: " + "left: " + left + " dx: " + dx); int newLeft = left;
    if(changedView == mLeftContent){
    // 把当前变化量传递给mMainContent
    newLeft = mMainContent.getLeft() + dx;
    } // 进行修正
    newLeft = fixLeft(newLeft); if(changedView == mLeftContent) {
    // 当左面板移动之后, 再强制放回去.
    mLeftContent.layout(0, 0, 0 + mWidth, 0 + mHeight);
    mMainContent.layout(newLeft, 0, newLeft + mWidth, 0 + mHeight);
    }
    // 更新状态,执行动画
    dispatchDragEvent(newLeft); // 为了兼容低版本, 每次修改值之后, 进行重绘
    invalidate();
    }
    // 4. 当View被释放的时候, 处理的事情(执行动画)
    @Override
    public void onViewReleased(View releasedChild, float xvel, float yvel) {
    // View releasedChild 被释放的子View
    // float xvel 水平方向的速度, 向右为+
    // float yvel 竖直方向的速度, 向下为+
    Log.d(TAG, "onViewReleased: " + "xvel: " + xvel + " yvel: " + yvel);
    super.onViewReleased(releasedChild, xvel, yvel); // 判断执行 关闭/开启
    // 先考虑所有开启的情况,剩下的就都是关闭的情况
    if(xvel == 0 && mMainContent.getLeft() > mRange / 2.0f){
    open();
    }else if (xvel > 0) {
    open();
    }else {
    close();
    } }
    @Override
    public void onViewDragStateChanged(int state) {
    // TODO Auto-generated method stub
    super.onViewDragStateChanged(state);
    }
    }; /**
    * 根据范围修正左边值
    * @param left
    * @return
    */
    private int fixLeft(int left) {
    if(left < 0){
    return 0;
    }else if (left > mRange) {
    return mRange;
    }
    return left;
    } protected void dispatchDragEvent(int newLeft) {
    float percent = newLeft * 1.0f/ mRange;
    //0.0f -> 1.0f
    Log.d(TAG, "percent: " + percent); if(mListener != null){
    mListener.onDraging(percent);
    } // 更新状态, 执行回调
    Status preStatus = mStatus;
    mStatus = updateStatus(percent);
    if(mStatus != preStatus){
    // 状态发生变化
    if(mStatus == Status.Close){
    // 当前变为关闭状态
    if(mListener != null){
    mListener.onClose();
    }
    }else if (mStatus == Status.Open) {
    if(mListener != null){
    mListener.onOpen();
    }
    }
    } // * 伴随动画:
    animViews(percent); }
    private Status updateStatus(float percent) {
    if(percent == 0f){
    return Status.Close;
    }else if (percent == 1.0f) {
    return Status.Open;
    }
    return Status.Draging;
    }
    private void animViews(float percent) {
    // > 1. 左面板: 缩放动画, 平移动画, 透明度动画
    // 缩放动画 0.0 -> 1.0 >>> 0.5f -> 1.0f >>> 0.5f * percent + 0.5f
    // mLeftContent.setScaleX(0.5f + 0.5f * percent);
    // mLeftContent.setScaleY(0.5f + 0.5f * percent);
    ViewHelper.setScaleX(mLeftContent, evaluate(percent, 0.5f, 1.0f));
    ViewHelper.setScaleY(mLeftContent, 0.5f + 0.5f * percent);
    // 平移动画: -mWidth / 2.0f -> 0.0f
    ViewHelper.setTranslationX(mLeftContent, evaluate(percent, -mWidth / 2.0f, 0));
    // 透明度: 0.5 -> 1.0f
    ViewHelper.setAlpha(mLeftContent, evaluate(percent, 0.5f, 1.0f)); // > 2. 主面板: 缩放动画
    // 1.0f -> 0.8f
    ViewHelper.setScaleX(mMainContent, evaluate(percent, 1.0f, 0.8f));
    ViewHelper.setScaleY(mMainContent, evaluate(percent, 1.0f, 0.8f)); // > 3. 背景动画: 亮度变化 (颜色变化)
    getBackground().setColorFilter((Integer)evaluateColor(percent, Color.BLACK, Color.TRANSPARENT), Mode.SRC_OVER);
    } /**
    * 估值器,0-100,一半50百分之=50,10-100
    * @param fraction
    * @param startValue
    * @param endValue
    * @return
    */
    public Float evaluate(float fraction, Number startValue, Number endValue) {
    float startFloat = startValue.floatValue();
    return startFloat + fraction * (endValue.floatValue() - startFloat);
    }
    /**
    * 颜色变化过度
    * @param fraction
    * @param startValue
    * @param endValue
    * @return
    */
    public Object evaluateColor(float fraction, Object startValue, Object endValue) {
    int startInt = (Integer) startValue;
    int startA = (startInt >> 24) & 0xff;
    int startR = (startInt >> 16) & 0xff;
    int startG = (startInt >> 8) & 0xff;
    int startB = startInt & 0xff;
    int endInt = (Integer) endValue;
    int endA = (endInt >> 24) & 0xff;
    int endR = (endInt >> 16) & 0xff;
    int endG = (endInt >> 8) & 0xff;
    int endB = endInt & 0xff;
    return (int)((startA + (int)(fraction * (endA - startA))) << 24) |
    (int)((startR + (int)(fraction * (endR - startR))) << 16) |
    (int)((startG + (int)(fraction * (endG - startG))) << 8) |
    (int)((startB + (int)(fraction * (endB - startB))));
    }
    @Override
    public void computeScroll() {
    super.computeScroll(); // 2. 持续平滑动画 (高频率调用)
    if(mDragHelper.continueSettling(true)){
    // 如果返回true, 动画还需要继续执行
    ViewCompat.postInvalidateOnAnimation(this);
    }
    } public void close(){
    close(true);
    }
    /**
    * 关闭
    */
    public void close(boolean isSmooth) {
    int finalLeft = 0;
    if(isSmooth){
    // 1. 触发一个平滑动画
    if(mDragHelper.smoothSlideViewTo(mMainContent, finalLeft, 0)){
    // 返回true代表还没有移动到指定位置, 需要刷新界面.
    // 参数传this(child所在的ViewGroup)
    ViewCompat.postInvalidateOnAnimation(this);
    }
    }else {
    mMainContent.layout(finalLeft, 0, finalLeft + mWidth, 0 + mHeight);
    }
    } public void open(){
    open(true);
    }
    /**
    * 开启
    */
    public void open(boolean isSmooth) {
    int finalLeft = mRange;
    if(isSmooth){
    // 1. 触发一个平滑动画
    if(mDragHelper.smoothSlideViewTo(mMainContent, finalLeft, 0)){
    // 返回true代表还没有移动到指定位置, 需要刷新界面.
    // 参数传this(child所在的ViewGroup)
    ViewCompat.postInvalidateOnAnimation(this);
    }
    }else {
    mMainContent.layout(finalLeft, 0, finalLeft + mWidth, 0 + mHeight);
    }
    }
    private int mHeight;
    private int mWidth;
    private int mRange; // b.传递触摸事件
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
    // 传递给mDragHelper
    return mDragHelper.shouldInterceptTouchEvent(ev);
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
    try {
    mDragHelper.processTouchEvent(event);
    } catch (Exception e) {
    e.printStackTrace();
    }
    // 返回true, 持续接受事件
    return true;
    } @Override
    protected void onFinishInflate() {
    super.onFinishInflate();
    // Github
    // 写注释
    // 容错性检查 (至少有俩子View, 子View必须是ViewGroup的子类) if(getChildCount() < 2){
    throw new IllegalStateException("布局至少有俩孩子. Your ViewGroup must have 2 children at least.");
    }
    if(!(getChildAt(0) instanceof ViewGroup && getChildAt(1) instanceof ViewGroup)){
    throw new IllegalArgumentException("子View必须是ViewGroup的子类. Your children must be an instance of ViewGroup");
    } mLeftContent = (ViewGroup) getChildAt(0);
    mMainContent = (ViewGroup) getChildAt(1);
    } @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    // 当尺寸有变化的时候调用 mHeight = getMeasuredHeight();
    mWidth = getMeasuredWidth(); // 移动的范围
    mRange = (int) (mWidth * 0.6f); } }

      MyLinearLayout:主页面,在打开侧边栏或者拖拽时不让主页面里的listview滑动

  4. public class MyLinearLayout extends LinearLayout {
    private DragLayout mDragLayout;
    public MyLinearLayout(Context context) {
    super(context);
    }
    public MyLinearLayout(Context context, AttributeSet attrs) {
    super(context, attrs);
    } public void setDraglayout(DragLayout mDragLayout){
    this.mDragLayout = mDragLayout;
    } @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
    // 如果当前是关闭状态, 按之前方法判断
    if(mDragLayout.getStatus() == Status.Close){
    return super.onInterceptTouchEvent(ev);
    }else {
    return true;
    }
    } @Override
    public boolean onTouchEvent(MotionEvent event) {
    // 如果当前是关闭状态, 按之前方法处理
    if(mDragLayout.getStatus() == Status.Close){
    return super.onTouchEvent(event);
    }else {
    // 手指抬起, 执行关闭操作
    if(event.getAction() == MotionEvent.ACTION_UP){
    mDragLayout.close();
    } return true;
    }
    }
    }

      MainActivity

  5. public class MainActivity extends Activity {
    private static final String TAG = "TAG";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    requestWindowFeature(Window.FEATURE_NO_TITLE);
    setContentView(R.layout.activity_main); final ListView mLeftList = (ListView) findViewById(R.id.lv_left);
    final ListView mMainList = (ListView) findViewById(R.id.lv_main);
    final ImageView mHeaderImage = (ImageView) findViewById(R.id.iv_header);
    MyLinearLayout mLinearLayout = (MyLinearLayout) findViewById(R.id.mll); // 查找Draglayout, 设置监听
    DragLayout mDragLayout = (DragLayout) findViewById(R.id.dl);
    // 设置引用
    mLinearLayout.setDraglayout(mDragLayout); mDragLayout.setDragStatusListener(new OnDragStatusChangeListener() { @Override
    public void onOpen() {
    Utils.showToast(MainActivity.this, "onOpen");
    // 左面板ListView随机设置一个条目
    Random random = new Random(); int nextInt = random.nextInt(50);
    mLeftList.smoothScrollToPosition(nextInt); } @Override
    public void onDraging(float percent) {
    Log.d(TAG, "onDraging: " + percent);// 0 -> 1
    // 更新图标的透明度
    // 1.0 -> 0.0
    ViewHelper.setAlpha(mHeaderImage, 1 - percent);
    } @Override
    public void onClose() {
    Utils.showToast(MainActivity.this, "onClose");
    // 让图标晃动
    // mHeaderImage.setTranslationX(translationX)
    ObjectAnimator mAnim = ObjectAnimator.ofFloat(mHeaderImage, "translationX", 15.0f);
    mAnim.setInterpolator(new CycleInterpolator(4));
    mAnim.setDuration(500);
    mAnim.start();
    }
    }); mLeftList.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, Cheeses.sCheeseStrings){//Cheeses自己定义的,存放的一些字符串
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
    View view = super.getView(position, convertView, parent);
    TextView mText = ((TextView)view);
    mText.setTextColor(Color.WHITE);
    return view;
    }
    }); mMainList.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, Cheeses.NAMES)); }
    }

      

7.侧滑、ViewDragHelper、属性动画的更多相关文章

  1. Android动画效果之Property Animation进阶(属性动画)

    前言: 前面初步认识了Android的Property Animation(属性动画)Android动画效果之初识Property Animation(属性动画)(三),并且利用属性动画简单了补间动画 ...

  2. Android动画效果之初识Property Animation(属性动画)

    前言: 前面两篇介绍了Android的Tween Animation(补间动画) Android动画效果之Tween Animation(补间动画).Frame Animation(逐帧动画)Andr ...

  3. Android属性动画

    这几天看郭神的博客 Android属性动画完全解析(上),初识属性动画的基本用法之后,我自己突然想实现一种动画功能,就是我们在携程网.阿里旅行等等手机APP端买火车票的时候,看到有选择城市,那么就有出 ...

  4. Android动画:模拟开关按钮点击打开动画(属性动画之平移动画)

    在Android里面,一些炫酷的动画确实是很吸引人的地方,让然看了就赏心悦目,一个好看的动画可能会提高用户对软件的使用率.另外说到动画,在Android里面支持3种动画: 逐帧动画(Frame Ani ...

  5. android 帧动画,补间动画,属性动画的简单总结

      帧动画——FrameAnimation 将一系列图片有序播放,形成动画的效果.其本质是一个Drawable,是一系列图片的集合,本身可以当做一个图片一样使用 在Drawable文件夹下,创建ani ...

  6. View动画和属性动画

    在应用中, 动画效果提升用户体验, 主要分为View动画和属性动画. View动画变换场景图片效果, 效果包括平移(translate), 缩放(scale), 旋转(rotate), 透明(alph ...

  7. Android属性动画源代码解析(超详细)

    本文假定你已经对属性动画有了一定的了解,至少使用过属性动画.下面我们就从属性动画最简单的使用开始. ObjectAnimator .ofInt(target,propName,values[]) .s ...

  8. ObjectAnimator属性动画应用demo

    感谢慕课网--eclipse_xu 布局文件:activity_main.xml <FrameLayout xmlns:android="http://schemas.android. ...

  9. 使用属性动画 — Property Animation

    属性动画,就是通过控制对象中的属性值产生的动画.属性动画是目前最高级的2D动画系统. 在API Level 11中添加.Property Animation号称能控制一切对象的动画,包括可见的和不可见 ...

随机推荐

  1. vim 中文乱码怎么解决

    一般来说只需要正确设置vim的编码识别序列就很少会遇到乱码问题: set fileencodings=ucs-bom,utf-8,utf-16,gbk,big5,gb18030,latin1 这个设置 ...

  2. linux下修改时间戳

    Linux下touch是一个非常有用的命令. touch语法结构如下: touch [-acfm][-d <日期时间>][-r <参考文件或目录>][-t <日期时间&g ...

  3. Pixel Recurrent Neural Networks翻译

    Pixel Recurrent Neural Networks 目前主要在用的文档存放: https://www.yuque.com/lart/papers/prnn github存档: https: ...

  4. Centos 安装 android sdk(转)

    原文地址: https://blog.csdn.net/kai_1215/article/details/80731099 这个后面有个指令没有运行起来,我做了一些修改: 原文:sdkmanager ...

  5. 五、Pyqt5事件、信号和槽

    PyQt中提供了两种针对事件处理的机制:一种是事件,另一种则是信号和槽. 一.事件 事件处理在PyQt中是比较底层的,常用的事件有键盘事件.鼠标事件.拖放事件.滚轮事件.定时事件.焦点事件.进入和离开 ...

  6. 第二阶段第九次spring会议

    今天我将对软件进行宠物信息的添加. 清屏功能 private void button5_Click(object sender, EventArgs e) { textBox2.Text = &quo ...

  7. ubuntu 重启nginx遇到错误

    错误如下:Job for nginx.service failed because the control process exited with error code. See "syst ...

  8. vue中使用axios

    1.结合vue-axios使用 vue-axios是按照vue插件的方式去写的,那么结合vue-axios就可以使用Vue.use()这个方法import axios from 'axios' imp ...

  9. Swift 加载 xib 崩溃问题

    新版本用 Swift开发 遇到的坑 解决方法

  10. DataTable调整顺序

    DataTable中手动调整列的顺序 DataTable中手动调整列的顺序(列序,reorder,Rearrange)DataTable dt = new DataTable(); dt.Column ...