编写不易,如有转载,请声明出处: 梦回河口:http://blog.csdn.net/zxc514257857/article/details/72602778
技术要点
- 自定义侧滑控件SlideMenu
- 主界面和菜单界面实现伴随移动
- 根据拖拽的百分比实现平移、缩放、滚动等动画效果
- ListView的基本使用
- Butterknife 的使用
Demo展示图片
布局代码
//(layout)activity_main
<com.test.slidemenu.view.SlideMenu
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/slideMenu"
android:background="@mipmap/bg"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!--两个子View-->
<!--引入菜单布局 通过getChildAt(0)获取-->
<include layout="@layout/layout_menu"/>
<!--引入主界面布局 通过getChildAt(1)获取-->
<include layout="@layout/layout_main"/>
</com.test.slidemenu.view.SlideMenu>
-------------------------------------------------------------------
//(layout)layout_main
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:background="#fff"
android:id="@+id/main_linearlayout"
android:layout_height="match_parent">
<FrameLayout
android:background="#18B4ED"
android:layout_width="match_parent"
android:layout_height="60dp">
<ImageView
android:layout_marginStart="20dp"
android:layout_width="50dp"
android:id="@+id/iv_headMainPic"
android:layout_gravity="center_vertical"
android:src="@mipmap/headpic"
android:layout_height="50dp"/>
</FrameLayout>
<ListView
android:overScrollMode="never"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/main_listview"/>
</LinearLayout>
-------------------------------------------------------------------
//(layout)layout_menu
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/menu_linearlayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<ImageView android:id="@+id/iv_headMenuPic"
android:layout_width="90dp"
android:layout_height="90dp"
android:background="@mipmap/headpic"
android:layout_marginTop="50dp"
android:layout_marginLeft="10dp"/>
<ListView android:layout_width="match_parent"
android:layout_marginTop="5dp"
android:layout_height="match_parent"
android:id="@+id/menu_listview"/>
</LinearLayout>
-------------------------------------------------------------------
//(layout)mainitem
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="60dp">
<ImageView
android:id="@+id/iv_pic"
android:src="@mipmap/pic"
android:layout_width="50dp"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginRight="15dp"/>
<TextView
android:id="@+id/tv_contact"
android:textSize="18sp"
android:textColor="#FF3E96"
android:layout_centerVertical="true"
android:layout_toRightOf="@+id/iv_pic"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</RelativeLayout>
</LinearLayout>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
自定义侧滑控件代码
//(view)SlideMenu
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.widget.FrameLayout;
/**
* 自定义SlideMenu控件 实现侧滑效果
* 为了避免自己去实现onMeasure和onLayout方法 我们继承FrameLayout,系统已经实现好了宽高的测量和位置的摆放
* 为什么不使用LinearLayout和RelativeLayout,因为FrameLayout代码简洁
*/
public class SlideMenu extends FrameLayout {
private static final String TAG = "SlideMenu";
// 通过它来进行View拖拽的实现
private ViewDragHelper mViewDragHelper;
private View mMenuView;
private View mMainView;
// mainView的拖拽范围
private int dragRange;
private int mMainWidth;
private int mMenuWidth;
private FloatEvaluator mFloatEvaluator;
private ArgbEvaluator mArgbEvaluator;
private OnSlideListener mOnSlideListener;
// 表示当前状态: 关闭
private DragState mDragState = DragState.Close;
public enum DragState{
Open ,Close
}
public SlideMenu(Context context) {
this(context , null);
}
public SlideMenu(Context context, AttributeSet attrs) {
this(context, attrs , 0);
}
public SlideMenu(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initData();
}
private void initData(){
// 父View、滑动敏感度、回调方法
mViewDragHelper = ViewDragHelper.create(this , callback);
// 浮点运算器
mFloatEvaluator = new FloatEvaluator();
// 颜色运算器
mArgbEvaluator = new ArgbEvaluator();
}
/**
* 该方法在ViewGroup将子View全部添加进来之后执行,但在onMeasure之前执行
* 一般用来初始化子View的引用,但是还不能获取到子View的宽高
*/
@Override
protected void onFinishInflate() {
mMenuView = getChildAt(0);
mMainView = getChildAt(1);
super.onFinishInflate();
}
/**
* 当onMeasure方法执行完之后执行,在该方法中可以获取所有控件的宽高
*/
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
dragRange = (int) (getMeasuredWidth()*0.6f);
mMainWidth = mMainView.getMeasuredWidth();
mMenuWidth = mMenuView.getMeasuredWidth();
super.onSizeChanged(w, h, oldw, oldh);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// 让ViewDragHelper帮助我们判断是否应该拦截
boolean result = mViewDragHelper.shouldInterceptTouchEvent(ev);
return result;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// 让ViewDragHelper帮助我们处理触摸事件
mViewDragHelper.processTouchEvent(event);
return true;
}
ViewDragHelper.Callback callback = new ViewDragHelper.Callback() {
/**
* 判断是否需要捕获View的触摸事件
* 这里的child都是当前触摸的View
*/
@Override
public boolean tryCaptureView(View child, int pointerId) {
// 捕获mainView和menuView的触摸事件
if(child == mMainView || child == mMenuView){
return true;
}
return false;
}
/**
* 当一个View被捕获触摸事件调用
*/
@Override
public void onViewCaptured(View capturedChild, int activePointerId) {
Log.i(TAG , "onViewCaptured:" + activePointerId);
super.onViewCaptured(capturedChild, activePointerId);
}
/**
* 通过返回值判断滑动方向 只要大于0就可以正常水平垂直滑动
*/
@Override
public int getViewHorizontalDragRange(View child) {
return 1;
}
/**
* 用于捕获子View在水平方向上的移动
* @param left:是ViewDragHelper帮我们计算好的View最新的left的值
* left = child.getLeft() + dx
* @return 真正想要View的Left变成的值
*/
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
// 限制主界面移动
if(child == mMainView){
left = clampLeft(left);
}
return left;
}
// /**
// * 用于捕获子View在垂直方向上的移动
// */
// @Override
// public int clampViewPositionVertical(View child, int top, int dy) {
// return top;
// }
/**
* 当View移动的时候调用,可以获取到手指移动的距离
*/
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
// 当手指在menuView移动的时候,让mainView进行一个伴随的移动,但是menuView不动
Log.i(TAG , "left: " + left + "dx: " + dx);
// menuView固定住
mMenuView.layout(0 ,0 , mMenuWidth , mMenuView.getBottom());
if(changedView == mMenuView){
int newLeft = mMainView.getLeft() + dx;
// 限制newLeft否则,在menuView上滑动mainView无限制
newLeft = clampLeft(newLeft);
// 移动mainView
mMainView.layout(newLeft ,0 , newLeft + mMainWidth , mMainView.getBottom());
}
// 增加伴随动画
float fraction = mMainView.getLeft() * 1.0f / dragRange;
// 得到百分比
Log.i(TAG , "fraction" + fraction);
// 执行动画效果
execAnim(fraction);
// 回调监听器的方法
if(fraction == 0f && mDragState !=DragState.Close){
mDragState =DragState.Close;
// 说明关闭了
if(mOnSlideListener != null){
mOnSlideListener.onClose();
}
// 说明打开了
}else if(fraction == 1f && mDragState !=DragState.Open){
mDragState =DragState.Open;
if(mOnSlideListener != null){
mOnSlideListener.onOpen();
}
}
// 说明在拖拽中
if(mOnSlideListener != null){
mOnSlideListener.onDraging(fraction);
}
super.onViewPositionChanged(changedView, left, top, dx, dy);
}
/**
* 当手指从View上抬起的时候执行
*/
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
// 判断mainView的left是否是大于dragRange的一半
if(mMainView.getLeft() > dragRange / 2){
// mainView平滑滚动到右边
mViewDragHelper.smoothSlideViewTo(mMainView , dragRange , 0 );
}else{
// mainView平滑滚动到左边
mViewDragHelper.smoothSlideViewTo(mMainView , 0 , 0 );
}
// 刷新操作
ViewCompat.postInvalidateOnAnimation(SlideMenu.this);
super.onViewReleased(releasedChild, xvel, yvel);
}
};
/**
* 根据拖拽的百分比来进行伴随动画效果
*/
private void execAnim(float fraction) {
// mainView的缩放
mMainView.setScaleX(mFloatEvaluator.evaluate(fraction , 1.0f , 0.8f));
mMainView.setScaleY(mFloatEvaluator.evaluate(fraction , 1.0f , 0.8f));
// menuView的缩放
mMenuView.setScaleX(mFloatEvaluator.evaluate(fraction , 0.4f , 1f));
mMenuView.setScaleY(mFloatEvaluator.evaluate(fraction , 0.4f , 1f));
// menuView 水平方向的平移
mMenuView.setTranslationX(mFloatEvaluator.evaluate(fraction ,-mMenuWidth /2, 0));
// 设置SlideMenu的颜色遮罩
getBackground();
if(getBackground() != null){
// 由黑色变为透明的遮罩效果
int color = (int) mArgbEvaluator.evaluate(fraction, Color.BLACK, Color.TRANSPARENT);
getBackground().setColorFilter(color, PorterDuff.Mode.DARKEN);
}
}
@Override
public void computeScroll() {
// 判断动画有没有结束,如果为true表示没有结束
if(mViewDragHelper.continueSettling(true)){
// 刷新操作
ViewCompat.postInvalidateOnAnimation(SlideMenu.this);
}
super.computeScroll();
}
/**
* 修正左边距
*/
private int clampLeft(int left) {
if(left > dragRange){
left = dragRange;
}else if(left < 0){
left = 0;
}
return left;
}
public interface OnSlideListener{
void onOpen();
void onClose();
void onDraging(float fraction);
}
public void setOnSlideListener(OnSlideListener mOnSlideListener){
this.mOnSlideListener = mOnSlideListener;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
- 228
- 229
- 230
- 231
- 232
- 233
- 234
- 235
- 236
- 237
- 238
- 239
- 240
- 241
- 242
- 243
- 244
- 245
- 246
- 247
- 248
- 249
- 250
- 251
- 252
- 253
- 254
- 255
- 256
- 257
- 258
- 259
- 260
- 261
- 262
- 263
- 264
- 265
- 266
- 267
- 268
- 269
- 270
- 271
- 272
- 273
- 274
- 275
- 276
常量类代码
//conf(Constant)
public class Constant {
public static final String[] SETTINGS = {"激活会员" , "QQ钱包" , "个性装扮" , "我的收藏" , "我的相册" , "我的文件"};
public static final String[] CONSTACTS = {
"宋江", "卢俊义","吴用","公孙胜","关胜","林冲","秦明","呼延灼","花荣","柴进","李应","朱仝",
"鲁智深","武松","董平","张清","扬志","徐宁","索超","戴宗","刘唐","李逵","史进","穆弘",
"雷横","李俊","阮小二","张横","阮小五","张顺","阮小七","杨雄","石秀","解珍","解宝","燕青",
"朱武","黄信","孙立","宣赞","赦思文","韩滔","彭玑","单廷","魏定国","萧让","裴宣","欧鹏",
"邓飞","燕顺","杨林","凌振","蒋敬","吕方","郭盛","安道全","皇浦端","王英","扈三娘","鲍旭",
"樊瑞","孔明","孔亮","项充","李衮","金大坚","马麟","童威","童猛","孟康","候建","陈达",
"杨春","郑天寿","陶宗旺","宋清","乐和","龚旺","丁得孙","穆春","曹正","宋万","杜迁","薛永",
"施恩","李忠","周通","汤隆","杜兴","邹渊","邹润","朱贵","朱富","蔡福","蔡庆","李立",
"李云","焦挺","石勇","孙新","顾大嫂","张青","孙二娘","王定六","郁保四","白胜","时迁","段景住"
};
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
activity代码
//(activity)MainActivity
import android.animation.FloatEvaluator;
import android.graphics.Color;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.TextView;
import android.widget.Toast;
import com.test.slidemenu.R;
import com.test.slidemenu.view.SlideMenu;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import butterknife.Bind;
import butterknife.ButterKnife;
import static com.test.slidemenu.conf.Constant.CONSTACTS;
import static com.test.slidemenu.conf.Constant.SETTINGS;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private FloatEvaluator mFloatEvaluator;
@Bind(R.id.iv_headMenuPic)
ImageView mIvHeadMenuPic;
@Bind(R.id.menu_listview)
ListView mMenuListview;
@Bind(R.id.menu_linearlayout)
LinearLayout mMenuLinearlayout;
@Bind(R.id.iv_headMainPic)
ImageView mIvHeadMainPic;
@Bind(R.id.main_listview)
ListView mMainListview;
@Bind(R.id.main_linearlayout)
LinearLayout mMainLinearlayout;
@Bind(R.id.slideMenu)
SlideMenu mSlideMenu;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initData();
}
private void initView() {
ButterKnife.bind(this);
mFloatEvaluator = new FloatEvaluator();
}
private void initData() {
mMenuListview.setAdapter(new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, SETTINGS) {
@NonNull
@Override
// 修改布局属性
public View getView(int position, View convertView, ViewGroup parent) {
TextView textView = (TextView) super.getView(position, convertView, parent);
textView.setTextColor(Color.parseColor("#FF3E96"));
textView.setTextSize(18);
textView.setSingleLine();
return textView;
}
});
mMenuListview.setDivider(null);
mMenuListview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
Toast.makeText(MainActivity.this, SETTINGS[i], Toast.LENGTH_SHORT).show();
}
});
List<Map<String, Object>> data = new ArrayList<>();
String[] from = {"pic", "contact" };
int[] to = {R.id.iv_pic, R.id.tv_contact};
mMainListview.setAdapter(new SimpleAdapter(MainActivity.this, data, R.layout.mainitem, from, to));
for (int i = 0; i < CONSTACTS.length; i++) {
Map<String, Object> map = new HashMap<>();
map.put("pic", R.mipmap.pic);
map.put("contact", CONSTACTS[i]);
data.add(map);
}
mMainListview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
Toast.makeText(MainActivity.this, CONSTACTS[i], Toast.LENGTH_SHORT).show();
}
});
mSlideMenu.setOnSlideListener(new SlideMenu.OnSlideListener() {
@Override
public void onOpen() {
Toast.makeText(MainActivity.this, "onOpen", Toast.LENGTH_SHORT).show();
}
@Override
public void onClose() {
Toast.makeText(MainActivity.this, "onClose", Toast.LENGTH_SHORT).show();
}
@Override
public void onDraging(float fraction) {
Log.i(TAG, "fraction:" + fraction);
// 沿着y轴旋转
mIvHeadMainPic.setRotationY(mFloatEvaluator.evaluate(fraction, 0, 720));
}
});
mIvHeadMenuPic.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(MainActivity.this, "我是menuView头像", Toast.LENGTH_SHORT).show();
}
});
mIvHeadMainPic.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(MainActivity.this, "我是mainView头像", Toast.LENGTH_SHORT).show();
}
});
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
注
使用Butterknife 需在Module的build.gradle中的dependencies节点下面添加:
compile 'com.jakewharton:butterknife:7.0.1'
- 1
目前最新版本为8.6.0。Butterknife配合ButterKnife Zelezny插件使用更佳
如果对Butterknife和ButterKnife Zelezny插件使用不太清楚可移步:Android中ButterKnife的使用(库和插件)http://blog.csdn.net/zxc514257857/article/details/59195960
Demo下载请移步:http://download.csdn.net/detail/zxc514257857/9848331
———-因本人才疏学浅,如博客或Demo中有错误的地方请大家随意指出,与大家一起讨论,共同进步,谢谢!