1.布局文件
<?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">
<com.itheima.draglayouthm91.DragLayout
android: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">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/head"/>
<ListView
android: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">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#18B4ED"
android:orientation="vertical"
android:padding="10dp"
>
<ImageView
android:id="@+id/iv_head"
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@drawable/head"/>
</LinearLayout>
<ListView
android: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() {
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return true;
}
//判断DragLayout的状态
@Override
public 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
@Override
public boolean tryCaptureView(View child, int pointerId) {
return child == main || child == menu;
}
//水平滑动的处理
//参数1:被捕获的孩子视图
//参数2:oldLeft+dx=targetLeft,新的left值
//参数3:dx,系统每隔一段时间检测手指的移动的距离
@Override
public 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就会负责处理拦截事件
@Override
public int getViewHorizontalDragRange(View child) {
return maxDragRange;
}
//当视图的位置发生改变的时候
//参数1:被改变的视图
@Override
public 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方向的速度
@Override
public 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为null
getBackground().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();
}
}
@Override
public void computeScroll() {
super.computeScroll();
//判断是否存在下一帧,如果有下一帧,移动过去
if (helper.continueSettling(true)) {
invalidate();
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
//将触摸事件交由helper处理
helper.processTouchEvent(event);
return true;
}
//拦截事件
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
//让helper决定是否拦截事件
return helper.shouldInterceptTouchEvent(ev);
}
//在所有的孩子视图加载完成之后回调这个方法
//只有在这个方法中才能获取所有的孩子
@Override
protected 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方法执行后执行,可以获取孩子的计算宽高
@Override
protected 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;
@Override
protected 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){
//根据每个位置返回对应的视图
@Override
public 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() {
@Override
public void onOpen() {
lv_menu_list.smoothScrollToPosition(new Random().nextInt(Cheeses.CHEESE_STRINGS.length));
}
@Override
public void onClose() {
ObjectAnimator oa=ObjectAnimator.ofFloat(iv_head,"translationX",0f,5f);
oa.setDuration(1000);
//动画插入器:周期插入器,可以改变动画的执行效果,左右摇摆七次
oa.setInterpolator(new CycleInterpolator(7));
oa.start();
}
@Override
public void onDragging(float percent) {
iv_head.setAlpha(1-percent);
}
});
main_no_touch_layout.setOnMainNoTouchListener(new MainNoTouchLayout.OnMainNoTouchListener() {
//接口的方法返回值由DragLayout的关闭状态决定
@Override
public 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[]{........}
}