1)应用场景:扩展主面板的功能
2)具体实现:
主要实现类ViewDragHelper,解决控件的拖拽问题。
里面有个参数mTouchSlop,最小敏感范围,值越小,越敏感
3)需要实现的动画
左面板动画,从小变大,从模糊到清楚,平移动画
右面板动画,从大到小,从清楚到模糊,平移动画
背景动画
———————————————————————
有需求者请加qq:136137465,非诚勿扰
(java 架构师全套教程,共760G, 让你从零到架构师,每月轻松拿3万)
01.高级架构师四十二个阶段高
02.Java高级系统培训架构课程148课时
03.Java高级互联网架构师课程
04.Java互联网架构Netty、Nio、Mina等-视频教程
05.Java高级架构设计2016整理-视频教程
06.架构师基础、高级片
07.Java架构师必修linux运维系列课程
08.Java高级系统培训架构课程116课时
(送:hadoop系列教程,java设计模式与数据结构, Spring Cloud微服务, SpringBoot入门)
——————————————————————–
1、创建类 DragLayout继承FrameLayout
public class DragLayout extends FrameLayout {
}
2、创建主文件
public class DragActivity extends Activity{
@Override
protected void onCreate(Bundle savedInstanceState) {
requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_drag);
}
}
3、在布局文件activity_drag把DragLayout作为容器
<com.android.imooc.draglayout.DragLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/bg"
android:orientation="vertical" >
<LinearLayout
android:id="@+id/ll_top"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
LinearLayout>
<LinearLayout
android:id="@+id/ll_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
LinearLayout>
com.android.imooc.draglayout.DragLayout>
4、初始化ViewDragHelper
DragHelper = ViewDragHelper.create(this, cb);
5、把事件传递给ViewDragHelper
@Override
public boolean onInterceptTouchEvent(android.view.MotionEvent ev) {
return mDragHelper.shouldInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mDragHelper.processTouchEvent(event);
return true;
}
6、初始化子控件并得到屏幕的宽高信息
@Override
protected void onFinishInflate() {
super.onFinishInflate();
if (getChildCount() < 2) {
throw new IllegalArgumentException("子控件不能少于两个");
}
if (!(getChildAt(0) instanceof ViewGroup && getChildAt(1) instanceof ViewGroup)) {
throw new IllegalArgumentException("子控件必须是ViewGroup的子类");
}
mLeftConent = (ViewGroup) getChildAt(0);
mMainConent = (ViewGroup) getChildAt(1);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mScreenHeight = getMeasuredHeight();
mScreenWidth = getMeasuredWidth();
mRange = (int) (mScreenWidth * 0.6f);
}
ViewDragHelper.Callback cb = new ViewDragHelper.Callback() {
/**
* 1.根据结果决定当前child是否可以拖拽
* @child当前可被拖拽的view
* @pointId手指的个数
*/
@Override
public boolean tryCaptureView(View child, int pointId) {
//return child == mMainConent;
return true;
}
/**
* 2.从左边拖动时,不让拖动时越界
*/
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
Logger.i(TAG, "clampViewPositionHorizontal left = " + left + ",dx = " + dx );
if (child == mMainConent) {
left = fixLeft(left);
}
return left;
}
/**
* 3.当view位置改变时的时候,需要处理的事情(更新状态,伴随动画,重绘界面)
*/
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
super.onViewPositionChanged(changedView, left, top, dx, dy);
Logger.i(TAG, "onViewPositionChanged left = " + left + ",top = " + top + ",dx = " + dx + ",dy = " +dy);
int newLeft = 0;
//固定左菜单,不让其移动,主内容移动
if (changedView == mLeftConent) {
mLeftConent.layout(0, 0, mScreenWidth, mScreenHeight);
//拖拽左边时,移动主内容
newLeft = mMainConent.getLeft() + dx;
newLeft = fixLeft(newLeft);
mMainConent.layout(newLeft, 0, newLeft + mScreenWidth, mScreenHeight);
}
//为了兼容底版本
invalidate();
}
手指释放时,
如果在中线的右边则layout到右边mrange
如果在中线的左边,则layout到0
1)在上面的监听器里重写方法
/**
* 4.view释放的时候触发
*
* @xvel 水平方向的速度
*/
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
Logger.i(TAG, "onViewReleased xvel = " + xvel + ",yvel = " + yvel);
// 如果在中线的右侧,并且没拖动时,则打开
if ((xvel == 0 && mMainConent.getLeft() > mRange / 2.0f) || xvel > 0) {
open();
} else {
close();
}
super.onViewReleased(releasedChild, xvel, yvel);
}
2)实现打开与关闭的方法
/**
* 关闭菜单
*/
protected void close(boolean isSmooth) {
if (isSmooth) {
// ObjectAnimator anim = ObjectAnimator.ofFloat(mMainConent,
// propertyName, values)
// 触发平滑动画
if (mDragHelper.smoothSlideViewTo(mMainConent, 0, 0)) {
// this 就是viewgroup
ViewCompat.postInvalidateOnAnimation(this);
}
} else {
mMainConent.layout(0, 0, mScreenWidth, mScreenHeight);
}
}
private void close() {
close(true);
}
/**
* 打开菜单
*/
protected void open(boolean isSmooth) {
if (isSmooth) {
// ObjectAnimator anim = ObjectAnimator.ofFloat(mMainConent,
// propertyName, values)
// 触发平滑动画
if (mDragHelper.smoothSlideViewTo(mMainConent, mScreenWidth, 0)) {
// this 就是viewgroup
ViewCompat.postInvalidateOnAnimation(this);
}
} else {
mMainConent.layout(mRange, 0, mScreenWidth + mRange, mScreenHeight);
}
}
private void open() {
open(true);
}
3)重写主类里的computeScroll方法
/**
* 持续的动画
*/
@Override
public void computeScroll() {
super.computeScroll();
// 如果返回true,动画还需继续执行
if (mDragHelper.continueSettling(true)) {
ViewCompat.postInvalidateOnAnimation(this);
}
}
为了能够实现缩小放到平移的动画,必须在onViewPositionChanged方法里添加dispatchDragEvent(newLeft)方法
protected void dispatchDragEvent(int newLeft) {
//获得百分比,有了百分比就可执行各种动画
float percent = newLeft * 1.0f / mRange;
Logger.d(TAG, "percent == " + percent);
//左面板动画,从小变大,从模糊到清楚,
//mLeftConent.setScaleX(0.5f * percent + 0.5f);
//mLeftConent.setScaleY(0.5f * percent + 0.5f);
ViewHelper.setScaleX(mLeftConent, evalute(percent, 0.5f, 1.0f));
ViewHelper.setScaleY(mLeftConent, evalute(percent, 0.5f, 1.0f));
// 平移动画 , -width/2 ->0 ,从外到内
ViewHelper.setTranslationX(mLeftConent, evalute(percent, -mScreenWidth/2.0f, 0));
//透明度从0.5f到 1.0f
ViewHelper.setAlpha(mLeftConent, evalute(percent, 0.5f, 1.0f));
//右面板动画,从大到小,从清楚到模糊,平移动画
ViewHelper.setScaleX(mMainConent, evalute(percent, 1.0f, 0.8f));
ViewHelper.setScaleY(mMainConent, evalute(percent, 1.0f, 0.8f));
//ViewHelper.setTranslationX(mMainConent, evalute(percent, 0, mScreenWidth/2.0f));
//背景动画 ,此动画必须在xml文件的主布局里添加背景图片
getBackground().setColorFilter((Integer) evaluateColor(percent, Color.BLACK, Color.TRANSPARENT), Mode.SRC_OVER);
}
/**
* 估值器,得到移动后的位置
* @param percent
* @param startValue
* @param endValue
* @return
*/
public Float evalute(float percent, Number startValue, Number endValue){
float start = startValue.floatValue();
return start + percent *(endValue.floatValue() - start);
}
/**
* 颜色变化过度
* @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))));
}
1)有三种状态,这里使用枚举进行定义
enum Status{
Close,Open,Draging
}
2)定义接口,主要是为了提供给用户在各个状态下实现相关的方法
public interface onDragStatusChangeListener{
void close();
void open();
void drag();
}
private onDragStatusChangeListener mListener;
public void setonDragStatusChangeListener(onDragStatusChangeListener listener){
this.mListener = listener;
}
3)更新状态,在dispatchDragEvent里
//更新状态
private void dispatchDragEvent()
{
mStatus = updateStatus(percent);
}
private Status updateStatus(float percent) {
return percent == 0 ? Status.CLOSE : (percent == 1? Status.OPEN : Status.DRAGING);
}
4)判断状态是否与以前的相同,不同则说明状态改变,进行回调
private void dispatchDragEvent()
{
if (mListener != null) {
mListener.drag(percent);
}
Status preStatus = mStatus;
//更新状态
mStatus = updateStatus(percent);
//说明状态改变
if (mStatus != preStatus) {
if (mStatus == Status.CLOSE) {
if (mListener != null) {
mListener.close();
}
}else if (mStatus == Status.OPEN) {
if (mListener != null) {
mListener.open();
}
}
}
}
1)首先给布局文件DragLayou添加id
2)在Activity里findViewById初始化DragLayou
3)使用dragLayout.setonDragStatusChangeListener(dragListenter)添加监听
代码:
public class DragActivity extends Activity{
@Override
protected void onCreate(Bundle savedInstanceState) {
requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_drag);
DragLayout dragLayout = (DragLayout) findViewById(R.id.draglayout);
dragLayout.setonDragStatusChangeListener(dragListenter);
}
private onDragStatusChangeListener dragListenter = new onDragStatusChangeListener() {
@Override
public void open() {
Util.showMsg(DragActivity.this, "open");
}
@Override
public void drag(float percent) {
Util.showMsg(DragActivity.this, "drag percent =" + percent);
}
@Override
public void close() {
Util.showMsg(DragActivity.this, "close");
}
};
}
1)修改布局:
<com.android.imooc.draglayout.DragLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/draglayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/bg"
android:orientation="vertical" >
<LinearLayout
android:id="@+id/ll_top"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_orange_light"
android:orientation="vertical" >
<ImageView
android:id="@+id/iv_head_left"
android:layout_width="48dp"
android:layout_height="48dp"
android:src="@drawable/head" />
<ListView
android:id="@+id/lv_left"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/iv_head_left"
android:cacheColorHint="@android:color/transparent" >
ListView>
LinearLayout>
<LinearLayout
android:id="@+id/ll_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_green_dark"
android:orientation="vertical" >
<RelativeLayout
android:id="@+id/ll_header"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#18B4ED"
android:gravity="center_vertical"
android:orientation="horizontal" >
<ImageView
android:id="@+id/iv_head"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginLeft="10dp"
android:scaleType="fitXY"
android:src="@drawable/head" />
<ImageView
android:id="@+id/iv_head_right"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_alignParentRight="true"
android:layout_marginRight="10dp"
android:src="@drawable/btn_right_selector" />
RelativeLayout>
<ListView
android:id="@+id/lv_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/ll_header" />
LinearLayout>
com.android.imooc.draglayout.DragLayout>
2)在activity里初始化数据
private void initListView() {
mLeftListView = (ListView) findViewById(R.id.lv_left);
mMainListView = (ListView) findViewById(R.id.lv_main);
mLeftListView.setAdapter(new ArrayAdapter(this, android.R.layout.simple_list_item_1, Cheeses.sCheeseStrings){
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = super.getView(position, convertView, parent);
if (view instanceof TextView) {
TextView tv = (TextView) view;
tv.setTextColor(Color.WHITE);
}
return view;
}
});
mMainListView.setAdapter(new ArrayAdapter(this, android.R.layout.simple_list_item_1, Cheeses.NAMES));
}
1)在主内容缩到最小的时候,listview里的内容不能上下拉动,如何实现这样的功能呢?
事件当然不能由listview来处理,只能由其父控件LinearLayout处理,所以只有重写LinearLayout里的两个方法onInterceptTouchEvent与onTouchEvent。
其中onInterceptTouchEvent是拦截动作,返回true时不把事件传递给子控件。
那如何把DragLayout里的事件交给LinearLayout处理,我们添加一个setDragLayout方法
具体代码:
/**
* @描述 TODO
* @项目名称 App_imooc
* @包名 com.android.imooc.draglayout
* @类名 XLinearLayout
* @author chenlin
* @date 2015年5月22日 下午10:41:03
*/
public class XLinearLayout extends LinearLayout {
private DragLayout mLayout;
public XLinearLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public XLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public XLinearLayout(Context context) {
super(context);
}
public void setDragLayout(DragLayout layout) {
mLayout = layout;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// 如果是当前是关闭状态,按之前的方法判断
if (mLayout.getStatus() == Status.CLOSE) {
return super.onInterceptTouchEvent(ev);
} else {
// 不把事件传递给子控件
return true;
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// 如果是当前是关闭状态,按之前的方法处理
if (mLayout.getStatus() == Status.CLOSE) {
return super.onTouchEvent(event);
} else {
//手指抬起
if (event.getAction() == MotionEvent.ACTION_UP) {
mLayout.close();
}
// 不把事件传递给子控件
return true;
}
}
}
2)然后把布局里的主面板的LinearLayout替换成com.android.imooc.draglayout.XLinearLayout
3 )在activity里把dragLayout设置到XLinearLayout
XLinearLayout xLinearLayout = (XLinearLayout) findViewById(R.id.ll_content);
xLinearLayout.setDragLayout(dragLayout);
链接: http://pan.baidu.com/s/1geXHVP5 密码: jg93