本文主要是介绍自定义控件之DragLayout仿QQ界面,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
1.布局文件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayoutxmlns: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"><com.itheima.draglayouthm91.DragLayoutandroid:background="@drawable/bg"android:id="@+id/dl"android:layout_width="match_parent"android:layout_height="match_parent"><include layout="@layout/menu" /><include layout="@layout/main" /></com.itheima.draglayouthm91.DragLayout>
</RelativeLayout>
2.菜单布局文件
<?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="vertical"><ImageViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@drawable/head"/><ListViewandroid:id="@+id/lv_menu_list"android:layout_width="match_parent"android:layout_height="match_parent"></ListView></LinearLayout>
3.main布局文件
<?xml version="1.0" encoding="utf-8"?>
<com.itheima.draglayouthm91.MainNoTouchLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@android:color/white"android:orientation="vertical"android:id="@+id/main_no_touch_layout"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:background="#18B4ED"android:orientation="vertical"android:padding="10dp"><ImageViewandroid:id="@+id/iv_head"android:layout_width="50dp"android:layout_height="50dp"android:src="@drawable/head"/></LinearLayout><ListViewandroid:id="@+id/lv_main_list"android:layout_width="match_parent"android:layout_height="match_parent"></ListView></com.itheima.draglayouthm91.MainNoTouchLayout>
4.由于在菜单打开时 main界面时不可以点击的 所以要把main布局的父布局 重写拦截事件和触摸事件
public class MainNoTouchLayout extends LinearLayout{public MainNoTouchLayout(Context context) {this(context,null);}public MainNoTouchLayout(Context context, AttributeSet attrs) {this(context, attrs,0);}public MainNoTouchLayout(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}private void init() {}@Overridepublic boolean onTouchEvent(MotionEvent event) {return true;}//判断DragLayout的状态@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {
// if(listener!=null){
// //判断在DragLayout没有关闭的时候拦截事件
// if(!listener.onMenuClose()){
// return true;
// }
// }
// return super.onInterceptTouchEvent(ev);return listener!=null&&!listener.onMenuClose();}private OnMainNoTouchListener listener;public interface OnMainNoTouchListener{boolean onMenuClose();}public void setOnMainNoTouchListener(OnMainNoTouchListener listener){this.listener=listener;}
}
5.自定义一个类继承FrameLayout
public class DragLayout extends FrameLayout {private ViewDragHelper helper;private View menu;private View main;private int maxDragRange;public DragLayout(Context context) {this(context, null);}public DragLayout(Context context, AttributeSet attrs) {this(context, attrs, 0);}public DragLayout(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}private void init() {//Google推出辅助开发者处理触摸事件的类,处理界面的滚动//参数1:helper为哪一个视图处理触摸事件//参数2:处理触摸事件后的回调helper = ViewDragHelper.create(this, callback);}//helper进行触摸处理的回调private ViewDragHelper.Callback callback = new ViewDragHelper.Callback() {//捕获视图,被捕获的视图可以进行触摸事件的处理//参数1:被捕获的孩子视图//参数2:pointerId,多指触摸的手指id@Overridepublic boolean tryCaptureView(View child, int pointerId) {return child == main || child == menu;}//水平滑动的处理//参数1:被捕获的孩子视图//参数2:oldLeft+dx=targetLeft,新的left值//参数3:dx,系统每隔一段时间检测手指的移动的距离@Overridepublic int clampViewPositionHorizontal(View child, int left, int dx) {//判断如果捕获的是main,处理main滑动的范围,不能小于0,不能大于最大范围if (child == main) {if (left > maxDragRange) {left = maxDragRange;} else if (left < 0) {left = 0;}}return left;}// @Override
// public int clampViewPositionVertical(View child, int top, int dy) {
// return top;
// }//获取水平的拖动范围,默认返回值0,如果重写为大于0的任意值,则helper就会负责处理拦截事件@Overridepublic int getViewHorizontalDragRange(View child) {return maxDragRange;}//当视图的位置发生改变的时候//参数1:被改变的视图@Overridepublic void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {super.onViewPositionChanged(changedView, left, top, dx, dy);//判断在滑动menu的时候,menu向前移动dx,让menu向反方向移动dx,这样menu相当于没有移动//在menu上移动dx,将这个dx交给main让main移动if (changedView == menu) {menu.offsetLeftAndRight(-dx);int oldLeft = main.getLeft();//计算新的left,限制newLeft,就达到限制newDx的目的int newLeft = oldLeft + dx;if (newLeft > maxDragRange) {newLeft = maxDragRange;} else if (newLeft < 0) {newLeft = 0;}int newDx = newLeft - oldLeft;main.offsetLeftAndRight(newDx);}//getleft()的意思Left position of this view relative to its parent.float percent = main.getLeft() * 1.0f / maxDragRange;executeAnimation(percent);//让图片透明度逐渐变化changeCurrentState(percent);}//当释放被捕获的视图的时候//参数1:被释放的孩子视图//参数2:释放孩子的瞬间的x方向的速度,向左为负,向右为正//参数3:释放孩子的瞬间的y方向的速度@Overridepublic void onViewReleased(View releasedChild, float xvel, float yvel) {super.onViewReleased(releasedChild, xvel, yvel);//判断速度为0,且释放的左边距大于最大边距的一半,则打开if (xvel == 0 && releasedChild.getLeft() > maxDragRange * 0.5f) {//打开open();} else if (xvel > 0) {//打开open();} else {//关闭close();}}};private DragState preState;private void changeCurrentState(float percent) {preState = currentState;if (percent == 0) {//关闭currentState = DragState.CLOSE;} else if (percent == 1) {//打开currentState = DragState.OPEN;} else {//拖拽中currentState = DragState.DRAGGING;}if (listener != null) {if (currentState == DragState.OPEN) {if (currentState != preState) {
// Log.i("test", "open----------");listener.onOpen();}} else if (currentState == DragState.CLOSE) {if (currentState != preState) {
// Log.i("test", "close----------");listener.onClose();}} else {
// Log.i("test", "dragging----------");//让ImageView,透明度逐渐变化listener.onDragging(percent);
// ImageView iv_head= (ImageView) findViewById(R.id.iv_head);
// Toast.makeText(getContext(),)}}}private OnDragStateChangedListener listener;//判断当前控件的状态是否是关闭状态public boolean isClose() {return currentState==DragState.CLOSE;}public interface OnDragStateChangedListener {void onOpen();void onClose();void onDragging(float percent);}public void setOnDragStateChangedListener(OnDragStateChangedListener listener) {this.listener = listener;}private void executeAnimation(float percent) {//percent:0-1//scale:1-0.8//TypeEvaluator:差值器,估值器//FloatEvaluator//处理main的缩放float evaluateResult = evaluate(percent, 1.0f, 0.8f);ViewCompat.setScaleX(main, evaluateResult);ViewCompat.setScaleY(main, evaluateResult);//处理menu的缩放evaluateResult = evaluate(percent, 0.6f, 1.0f);ViewCompat.setScaleX(menu, evaluateResult);ViewCompat.setScaleY(menu, evaluateResult);float range = -maxDragRange * 0.8f;evaluateResult = evaluate(percent, range, 0);ViewCompat.setTranslationX(menu, evaluateResult);//颜色从黑色渐变到透明色int color = (int) evaluateColor(percent, Color.BLACK, Color.TRANSPARENT);//参数2:后来的颜色覆盖之前的颜色//外层要有背景,否则getBackground为nullgetBackground().setColorFilter(color, PorterDuff.Mode.SRC_OVER);}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))));}// private int price=1;
// //表示打开状态
// private static final int open=1;
// private static final int close=2;
// private static final int dragging=3;
// private static final String OPEN="abc";
// private String price="abc";//枚举里都是常量,但是这个常量不占用任何的基本数据类型的值//声明默认状态为关闭private DragState currentState = DragState.CLOSE;public enum DragState {OPEN, CLOSE, DRAGGING}public float evaluate(float fraction, float startValue, float endValue) {return startValue + fraction * (endValue - startValue);}private void close() {if (helper.smoothSlideViewTo(main, 0, 0)) {invalidate();}}private void open() {//判断menu是否已经移动到了最终的位置if (helper.smoothSlideViewTo(main, maxDragRange, 0)) {invalidate();}}@Overridepublic void computeScroll() {super.computeScroll();//判断是否存在下一帧,如果有下一帧,移动过去if (helper.continueSettling(true)) {invalidate();}}@Overridepublic boolean onTouchEvent(MotionEvent event) {//将触摸事件交由helper处理helper.processTouchEvent(event);return true;}//拦截事件@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {//让helper决定是否拦截事件return helper.shouldInterceptTouchEvent(ev);}//在所有的孩子视图加载完成之后回调这个方法//只有在这个方法中才能获取所有的孩子@Overrideprotected void onFinishInflate() {super.onFinishInflate();//代码健壮性处理//要求DragLayout有且仅有两个孩子if (getChildCount() != 2) {throw new RuntimeException("have you follow my suggestion?bastard?fuc?");}if (!(getChildAt(0) instanceof ViewGroup) || !(getChildAt(1) instanceof ViewGroup)) {throw new RuntimeException("Your children must be instance of ViewGroup?You know?");}menu = getChildAt(0);main = getChildAt(1);}//这个方法是在onMeasure方法执行后执行,可以获取孩子的计算宽高@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);maxDragRange = (int) (main.getMeasuredWidth() * 0.6f);}}
6.activity 处理显示数据和监听事件
public class MainActivity extends AppCompatActivity {private ListView lv_main_list;private ListView lv_menu_list;private ImageView iv_head;private DragLayout dl;private MainNoTouchLayout main_no_touch_layout;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);init();}private void init() {initView();initDatas();}private void initDatas() {ArrayAdapter<String> adapter=new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,Cheeses.NAMES);lv_main_list.setAdapter(adapter);adapter=new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,Cheeses.CHEESE_STRINGS){//根据每个位置返回对应的视图@Overridepublic View getView(int position, View convertView, ViewGroup parent) {View view = super.getView(position, convertView, parent);TextView tv= (TextView) view;tv.setTextColor(Color.WHITE);return view;}};lv_menu_list.setAdapter(adapter);dl.setOnDragStateChangedListener(new DragLayout.OnDragStateChangedListener() {@Overridepublic void onOpen() {lv_menu_list.smoothScrollToPosition(new Random().nextInt(Cheeses.CHEESE_STRINGS.length));}@Overridepublic void onClose() {ObjectAnimator oa=ObjectAnimator.ofFloat(iv_head,"translationX",0f,5f);oa.setDuration(1000);//动画插入器:周期插入器,可以改变动画的执行效果,左右摇摆七次oa.setInterpolator(new CycleInterpolator(7));oa.start();}@Overridepublic void onDragging(float percent) {iv_head.setAlpha(1-percent);}});main_no_touch_layout.setOnMainNoTouchListener(new MainNoTouchLayout.OnMainNoTouchListener() {//接口的方法返回值由DragLayout的关闭状态决定@Overridepublic boolean onMenuClose() {return dl.isClose();}});}private void initView() {lv_main_list = (ListView) findViewById(R.id.lv_main_list);lv_menu_list = (ListView) findViewById(R.id.lv_menu_list);iv_head = (ImageView) findViewById(R.id.iv_head);dl = (DragLayout) findViewById(R.id.dl);main_no_touch_layout = (MainNoTouchLayout) findViewById(R.id.main_no_touch_layout);}
}
7.定义个常量类
Cheeses{
public static final String[] CHEESE_STRINGS{....}public static final String[] NAMES = new String[]{........}
}
这篇关于自定义控件之DragLayout仿QQ界面的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!