仿qq的侧拉菜单效果
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了仿qq的侧拉菜单效果相关的知识,希望对你有一定的参考价值。
自定义控件 import android.animation.ArgbEvaluator; import android.animation.FloatEvaluator; import android.content.Context; import android.graphics.Color; import android.graphics.PorterDuff; import android.support.v4.view.ViewCompat; import android.support.v4.widget.ViewDragHelper; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.animation.ScaleAnimation; import android.widget.FrameLayout; import android.widget.Scroller; /** * 让SlideMen去继承系统已有的布局,目的是为了让他们帮我们实现onMeasure方法, * 一般的话我们会选择继承FrameLayout,因为FrameLayout最轻量级 */ public class SlideMenu extends FrameLayout { ViewDragHelper dragHelper; private View menu; private View main; int maxLeft; ArgbEvaluator argbEval = new ArgbEvaluator(); FloatEvaluator floatEval = new FloatEvaluator(); public SlideMenu(Context context) { this(context, null); } public SlideMenu(Context context, AttributeSet attrs) { this(context, attrs, 0); } public SlideMenu(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); dragHelper = ViewDragHelper.create(this,callback); } /** * 该方法是当前view在布局文件中的xml结束标签读取完后执行,此时就知道 * 当前View有几个子View了,但是注意:此时还不能获取子View的宽高,因为 * 还木有测量呢! */ @Override protected void onFinishInflate() { super.onFinishInflate(); menu = getChildAt(0); main = getChildAt(1); } /** * 该方法是onMeasure执行之后执行,因此可获取宽高 * @param w * @param h * @param oldw * @param oldh */ @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); maxLeft = (int) (getMeasuredWidth()*0.6f); } // /** // * 用来测量自己和自己的子View的 // * @param widthMeasureSpec // * @param heightMeasureSpec // */ // @Override // protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // super.onMeasure(widthMeasureSpec, heightMeasureSpec); // //只需要测量子View即可 // // for (int i = 0; i < getChildCount(); i++) { // View child = getChildAt(i); // measureChild(child,widthMeasureSpec,heightMeasureSpec); // } // } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { //让ViewDragHelper帮助我们判断是否应该拦截 boolean result = dragHelper.shouldInterceptTouchEvent(ev); return result; } @Override public boolean onTouchEvent(MotionEvent event) { //让ViewDragHelper帮我们处理触摸事件 dragHelper.processTouchEvent(event); return true; } ViewDragHelper.Callback callback = new ViewDragHelper.Callback() { /** * 尝试监视View的触摸事件 * @param child 当前触摸的子View * @param pointerId 手指多点触摸时的触摸点的索引 * @return true表示监视 ,false就是忽略不管 */ @Override public boolean tryCaptureView(View child, int pointerId) { return child==main || child==menu; } /** * 看起来是获取View水平的拖拽范围的,然而并不是这样,这是一个鸡肋的方法,目前它的作用是 * 用来判断你是否想强制水平滑动的,如果想强制水平滑动,则返回大于0的任意值 * @param child * @return */ @Override public int getViewHorizontalDragRange(View child) { return 1; } /** * 修正修改View水平方向的位置移动,控制水平移动的 * @param child 当前触摸的子View * @param left ViewDragHelper认为我想让View的left变成的值,它是这样计算好的:child.getLeft+dx * @param dx 本次手指移动的水平距离 * @return 最终返回的值表示我们真正想让child的left变成的值 */ @Override public int clampViewPositionHorizontal(View child, int left, int dx) { //只限制main if(child==main){ left = clampLeft(left); } return left; } /** * 修正修改View垂直方向的位置移动,控制垂直移动的 * @param child 当前触摸的子View * @param top ViewDragHelper认为我想让View的top变成的值,它是这样计算好的:child.getTop+dy * @param dy 本次手指移动的垂直距离 * @return 最终返回的值表示我们真正想让child的top变成的值 */ @Override public int clampViewPositionVertical(View child, int top, int dy) { return 0; } /** * 当View位置改变的时候执行 * @param changedView 当前位置改变的View * @param left 当前VIew改变后最新的left * @param top 当前VIew改变后最新的top * @param dx 本次移动的水平距离 * @param dy 本次移动的垂直距离 */ @Override public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { super.onViewPositionChanged(changedView, left, top, dx, dy); // Log.e("tag","left: "+left + " dx: "+dx); //根据dx,让main进行伴随的移动 if(changedView==menu){ //手动让menu固定在原点位置 menu.layout(0,0,menu.getMeasuredWidth(),menu.getBottom()); int newLeft = main.getLeft()+dx; //对newLeft进行限制 newLeft = clampLeft(newLeft); main.layout(newLeft,0,newLeft+main.getMeasuredWidth(), main.getBottom()); } //执行动画 //1.获取main拖动的百分比:0-1f float fraction = main.getLeft()*1f / maxLeft; //2.根据百分比去执行一些列的伴随的动画 execAnim(fraction); //3.回调接口的方法 if(listener!=null){ listener.onSliding(fraction); if(fraction==0f){ listener.onClose(); }else if(fraction==1f){ listener.onOpen(); } } } /** * 手指抬起的时候执行 * @param releasedChild 抬起的那个子View * @param xvel x方向滑动的速度,单位是px/s * @param yvel y方向滑动的速度 */ @Override public void onViewReleased(View releasedChild, float xvel, float yvel) { super.onViewReleased(releasedChild, xvel, yvel); // Log.e("tag","xvel: "+xvel); if(main.getLeft()>maxLeft/2){ //要open openMenu(); }else { //close closeMenu(); } } }; /** * 执行伴随动画 * @param fraction */ private void execAnim(float fraction) { //fraction:0f - 1f //main执行缩放动画 //scale: 1f - 0.8f //算法: startVal + (endVal-startVal)*fraction float scale = floatEval.evaluate(fraction,1f,0.8f); main.setScaleY(scale); main.setScaleX(scale); //menu执行缩放 menu.setScaleX(floatEval.evaluate(fraction,0.3f,1f)); menu.setScaleY(floatEval.evaluate(fraction,0.3f,1f)); //menu执行平移 menu.setTranslationX(floatEval.evaluate(fraction,-menu.getMeasuredWidth()/2,0)); //立体3D效果 // main.setRotationY(floatEval.evaluate(fraction,0,90)); // menu.setRotationY(floatEval.evaluate(fraction,-90,0)); //给SlideMenu的背景图片添加阴影遮罩效果 if(getBackground()!=null){ int color = (int) argbEval.evaluate(fraction,Color.BLACK,Color.TRANSPARENT); getBackground().setColorFilter(color,PorterDuff.Mode.SRC_OVER); } } /** * 关闭菜单 */ public void closeMenu() { dragHelper.smoothSlideViewTo(main,0,0); ViewCompat.postInvalidateOnAnimation(SlideMenu.this); } /** * 打开菜单 */ public void openMenu() { // scroller.startScroll(); // invalidate(); //ViewDragHelper的写法是这样滴: dragHelper.smoothSlideViewTo(main,maxLeft,0); ViewCompat.postInvalidateOnAnimation(SlideMenu.this); } // Scroller scroller = null; @Override public void computeScroll() { super.computeScroll(); // if(scroller.computeScrollOffset()){ // //如果动画还没有结束 // scrollTo(scroller.getCurrX(),scroller.getCurrY()); // invalidate(); // } if(dragHelper.continueSettling(true)){ ViewCompat.postInvalidateOnAnimation(SlideMenu.this); } } /** * 判断left的值 * @param left * @return */ private int clampLeft(int left) { if(left<0){ left = 0; }else if(left>maxLeft){ left = maxLeft; } return left; } OnSlideListener listener; public void setOnSlideListener(OnSlideListener listener){ this.listener = listener; } //定义回调接口 public interface OnSlideListener{ void onSliding(float fraction); void onOpen(); void onClose(); } }
import android.animation.ObjectAnimator;
import android.graphics.Color;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.view.ViewCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.BounceInterpolator;
import android.view.animation.CycleInterpolator;
import android.view.animation.OvershootInterpolator;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import butterknife.Bind;
import butterknife.ButterKnife;
public class MainActivity extends AppCompatActivity {
@Bind(R.id.menu_listview)
ListView menuListview;
@Bind(R.id.iv_head)
ImageView ivHead;
@Bind(R.id.main_listview)
ListView mainListview;
@Bind(R.id.slidemenu)
SlideMenu slidemenu;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
//填充数据
mainListview.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1
, Constant.NAMES));
menuListview.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1
, Constant.sCheeseStrings) {
@NonNull
@Override
public View getView(int position, View convertView, ViewGroup parent) {
TextView view = (TextView) super.getView(position, convertView, parent);
view.setTextColor(Color.WHITE);//偷梁换柱
return view;
}
});
//添加滑动监听器
slidemenu.setOnSlideListener(new SlideMenu.OnSlideListener() {
@Override
public void onSliding(float fraction) {
ivHead.setRotation(720*fraction);
}
@Override
public void onOpen() {
Toast.makeText(MainActivity.this, "芝麻开门!", Toast.LENGTH_SHORT).show();
}
@Override
public void onClose() {
Toast.makeText(MainActivity.this, "芝麻关门-----!", Toast.LENGTH_SHORT).show();
ViewCompat.animate(ivHead)
.translationX(60)
// .setInterpolator(new CycleInterpolator(4))//循环执行
// .setInterpolator(new OvershootInterpolator(4))//超过一点再回来
.setInterpolator(new BounceInterpolator())//当当当当~~~~
.setDuration(1000)
.start();
}
});
}
}
以上是关于仿qq的侧拉菜单效果的主要内容,如果未能解决你的问题,请参考以下文章