zoukankan      html  css  js  c++  java
  • 7.侧滑、ViewDragHelper、属性动画

    实现这样的效果:
    ## 侧滑面板(对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));
      		
      		
      		
      	}
      }
      

        

  • 相关阅读:
    pip install urllib2不能安装
    linux 基础信息查询
    gitlab 操作指南
    ubuntu 新手入门第一天
    颜色配色
    mvn mybatis-generator:generate postgresql
    sublime 列编辑模式
    mybatis-generator 根据表生成对应文件
    Spring MVC Mock demo
    Toad常用快捷键
  • 原文地址:https://www.cnblogs.com/sixrain/p/5041939.html
Copyright © 2011-2022 走看看