山寨风,高仿大街app拖动删除或收藏效果来袭!

来源:互联网 发布:域名查询工具nslookup 编辑:程序博客网 时间:2024/06/01 08:47

嗯,首先自我介绍一下,本人目前在校学生,安卓开发小菜一个,这是我的第一篇博客,希望能以这篇博客为起点,纪录自己的成长,也与各位安卓新手共勉。

进入正题,秉着无图无jb的观点,先上个效果图给各位看官看下,有兴趣的继续看,没兴趣的请便,o(^▽^)o。

总体模仿的还是挺像的,个人认为。

主要功能分析

1.对布局的旋转、透明度等的处理

2.自定义各种形状的图片(例如圆形图片)

3.图片的高斯模糊

4.动画处理


界面分析

其实刚开始想模仿这个界面的时候,我考虑的是ViewPager,不过他这个界面的功能是,不管你右滑还是左滑,这条item都会删掉,想想ViewPager好像有点麻烦,所以就放弃咯。
废话不多说,我直接说我的实现方法,主界面是一个FrameLayout,然后通过代码动态addView和removeView添加和删除item,当左滑或右滑到超过一定范围,便删除FrameLayout的最后一个item,并且重新添加一个到FrameLayout的第一个位置。根据手指在屏幕移动的距离来控制透明度以及旋转的变化。
不知道你们有没有看到,背景还有类似几张纸叠在一起的效果,这个是我自己P的背景图,不是用代码实现的。如果没看到,在文末自行下载运行看看。好了,说了这么多,接下来说下代码的讲解。

功能实现

主界面布局activity_main.xml

<?xml version="1.0" encoding="utf-8"?><RelativeLayout    android:id="@+id/rlRoot"    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"    android:gravity="center"    tools:context="com.chenantoa.main.MainActivity">    <FrameLayout        android:id="@+id/flContainer"        android:layout_width="match_parent"        android:paddingTop="10dp"        android:paddingBottom="20dp"        android:layout_height="match_parent"        android:layout_centerInParent="true">        <ImageView            android:layout_gravity="center_horizontal"            android:src="@drawable/bg"            android:layout_width="350dp"            android:layout_height="480dp"/>    </FrameLayout></RelativeLayout>


很简单,没什么好说的,一个FrameLayout,这是一个布局,用来陈放item的,里面的ImageView是个什么东西呢?是一个背景图片,也就是刚才上面所说的类似几张纸叠起来的效果,有人可能会问为什么不直接在FrameLayout写个backgroud呢?我一开始也是这么做的,那么我必须把FrameLayout的大小定义成容器的大小,也就是不能写成match_parent了,这里就出问题了。当容器里面的item旋转的时候,超过容器的那部分将不会显示。

不过我自己实验过,如果一开始就在FrameLayout声明好item,而不是动态添加的话,就算移动出了容器的范围,也能够显示,不知道为什么,有知道的大神可以在下面留言,谢谢。

item布局 stack_item.xml

<?xml version="1.0" encoding="utf-8"?><FrameLayout    android:id="@+id/llItem"    xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    android:layout_width="350dp"    android:layout_height="500dp">    <LinearLayout        android:layout_width="350dp"        android:layout_height="500dp"        android:background="@drawable/item_bg"        android:gravity="center_horizontal"        android:orientation="vertical">        <FrameLayout            android:id="@+id/flAvater"            android:layout_width="match_parent"            android:layout_height="140dp">            <com.chenantao.view.widget.MultipleShapeImg                android:id="@+id/blurAvatar"                android:layout_width="match_parent"                android:layout_height="130dp"                android:src="@mipmap/avatar"                app:type="arcRectangle"/>            <com.chenantao.view.widget.MultipleShapeImg                android:id="@+id/roundAvatar"                android:layout_width="75dp"                android:layout_height="75dp"                android:layout_gravity="center|bottom"                android:src="@mipmap/avatar"                app:type="round"/>            <TextView                android:id="@+id/tvUsername"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:layout_gravity="center_horizontal"                android:layout_marginTop="10dp"                android:textColor="@android:color/white"                android:textSize="20sp"/>        </FrameLayout>        <TextView            android:id="@+id/tvSchool"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_marginTop="5dp"            android:text="some message"            android:textColor="@android:color/black"            android:textSize="15sp"/>        <TextView            android:id="@+id/tvMajor"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_marginTop="5dp"            android:text="some message"            android:textSize="15sp"/>        <View            android:layout_width="300dp"            android:layout_height="1px"            android:layout_gravity="center_horizontal"            android:layout_marginTop="25dp"            android:background="@android:color/darker_gray"/>        <LinearLayout            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:layout_marginTop="5dp"            android:gravity="center"            android:orientation="horizontal">            <TextView                android:id="@+id/tvEntranceTime"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:layout_marginTop="5dp"                android:text="2013级入学"                android:textSize="15sp"/>            <View                android:layout_width="1dp"                android:layout_height="23dp"                android:layout_marginLeft="10dp"                android:layout_marginRight="40dp"                android:layout_marginTop="3dp"                android:background="@android:color/darker_gray"/>            <TextView                android:id="@+id/tvAdress"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:layout_marginTop="5dp"                android:text="广州"                android:textSize="15sp"/>        </LinearLayout>        <View            android:layout_width="300dp"            android:layout_height="1px"            android:layout_gravity="center_horizontal"            android:layout_marginTop="25dp"            android:background="@android:color/darker_gray"/>        <TextView            android:id="@+id/tvSkill"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_marginTop="5dp"            android:text="some message"            android:textSize="15sp"/>    </LinearLayout>    <ImageView        android:id="@+id/ivIgnore"        android:layout_width="200dp"        android:layout_height="200dp"        android:layout_gravity="center"        android:src="@mipmap/ignore"        android:visibility="gone"/>    <ImageView        android:id="@+id/ivInterested"        android:layout_width="200dp"        android:layout_height="200dp"        android:layout_gravity="center"        android:src="@mipmap/interested"        android:visibility="gone"/></FrameLayout>

嗯,这个由点小长,不过都很简单,相信有点android基础的都能分析出item的布局。解释一下第一个FrameLayout里面的两个MultipleShapeImg是什么鬼,这是一个自定义ImageView,目的是实现圆形的图片以及那个下边是一条曲线的矩形图片,这个后边会说。接下来开始看java代码。

主界面Activity MainActivity.java

private FrameLayout mContainer;//framelayout容器private float mRotateFactor;//控制item旋转范围private double mItemAlphaFactor;//控制item透明度变化范围private double mItemIvAlphaFactor;//控制item上面的图片的透明度变化范围private float mLimitTranslateX = 100;//限制移动距离,当超过这个距离的时候,删除该itemprivate List<User> mDatas;//item的数据private int mIndex = -1;//标识当前读取到数据的第几个下标

先定义一些变量,现在看不懂耶没关系,后面实现的时候就懂了。在onCreate的时候对他们进行初始化。并且利用getDatas方法生成一些伪数据。这个比较简单,就不贴代码了,主要就是定义一个bean User类,然后进行一些伪数据赋值。
@Overrideprotected void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mDatas = GenerateData.getDatas();//从屏幕的最右边滑动到最左边,最多旋转60度int screenWidth = ScreenUtils.getScreenWidth(this);mRotateFactor = 60 * 1.0f / screenWidth;//左滑,透明度最少到0.3fmItemAlphaFactor = 0.7 * 1.0f / screenWidth / 2;//item上面图标透明度的变化,滑动4分之一屏幕的距离便使其完全显示mItemIvAlphaFactor = 4.0f / screenWidth;mContainer = (FrameLayout) findViewById(R.id.flContainer);}



定义个方法,用于添加view
public void addViewToBehind(){if (mIndex == mDatas.size() - 1){return;}//加载布局final View item = LayoutInflater.from(this).inflate(R.layout.stack_item, null);FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(dp2px(350), dp2px(470), Gravity.CENTER_HORIZONTAL);item.setLayoutParams(lp);mContainer.addView(item, 1);//初始化item的数据User user = mDatas.get(++mIndex);//取出一条数据,并且增加index的下标ImageView roundAvatar = (ImageView) item.findViewById(R.id.roundAvatar);ImageView blurAvatar = (ImageView) item.findViewById(R.id.blurAvatar);//Log.e("cat", "avatar resId:" + user.getAvater());CatImageLoader.getInstance().loadImage(user.getAvater(), blurAvatar);CatImageLoader.getInstance().loadImage(user.getAvater(), roundAvatar);TextView tvUsername = (TextView) item.findViewById(R.id.tvUsername);TextView tvSchool = (TextView) item.findViewById(R.id.tvSchool);TextView tvMajor = (TextView) item.findViewById(R.id.tvMajor);TextView tvEntranceTime = (TextView) item.findViewById(R.id.tvEntranceTime);TextView tvSkill = (TextView) item.findViewById(R.id.tvSkill);final ImageView ivIgnore = (ImageView) item.findViewById(R.id.ivIgnore);final ImageView ivInterested = (ImageView) item.findViewById(R.id.ivInterested);tvUsername.setText(user.getName());tvSchool.setText(user.getSchool());tvMajor.setText(user.getMajor() + " | " + user.getSchoolLevel());tvEntranceTime.setText(user.getEntranceTime());tvSkill.setText("装逼 吹牛逼");//设置item的重心,主要是旋转的中心item.setPivotX(item.getLayoutParams().width / 2);item.setPivotY(item.getLayoutParams().height * 2);item.setOnTouchListener(new View.OnTouchListener(){float touchX, distanceX;@Overridepublic boolean onTouch(View v, MotionEvent event){switch (event.getAction()){case MotionEvent.ACTION_DOWN:touchX = event.getRawX();break;case MotionEvent.ACTION_MOVE:distanceX = event.getRawX() - touchX;item.setRotation(distanceX * mRotateFactor);//alpha scale 1~0.3//item的透明度为从1到0.3item.setAlpha(1 - (float) Math.abs(mItemAlphaFactor * distanceX));if (distanceX < 0)//如果为左滑{//显示忽略图标,隐藏感兴趣图标ivIgnore.setVisibility(View.VISIBLE);ivInterested.setVisibility(View.GONE);ivIgnore.setAlpha((float) (Math.abs(distanceX) * mItemIvAlphaFactor));} else{//显示感兴趣图标,隐藏忽略图标ivIgnore.setVisibility(View.GONE);ivInterested.setVisibility(View.VISIBLE);ivInterested.setAlpha((float) (distanceX * mItemIvAlphaFactor));}break;case MotionEvent.ACTION_UP:if (Math.abs(distanceX) > mLimitTranslateX){View removeView = mContainer.getChildAt(mContainer.getChildCount() -1);removeView(removeView, distanceX < 0 ? true : false);addViewToBehind();} else{//复位item.setRotation(0);item.setAlpha(1);ivIgnore.setAlpha(1.0f);ivInterested.setAlpha(1.0f);ivIgnore.setVisibility(View.GONE);ivInterested.setVisibility(View.GONE);}break;}return true;}});}

嗯,代码挺长,不过不用紧张,都是一些简单的代码,注释已经写的挺清楚了,我简单解释一下。注意这里,由于我们的FrameLayout方法里面有个ImageView作为背景图片,所以我们添加图片的时候,默认要从FrameLayout的下标为1的位置开始添加。
注意这里,由于我们的FrameLayout方法里面有个ImageView作为背景图片,所以我们添加图片的时候,默认要从FrameLayout的下标为1的位置开始添加。删除view就简单了,直接移除FrameLayout里面下标为child长度减1的view。首先使用LayoutInflater加载我们的item布局,然后给他设置LayoutParams并且添加到我们的容器下标为1的位置中去。接着根据下标取出对应的bean,然后对item上面的控件进行赋值。里面设置图片的时候使用了一个CatImageLoader.loadImg()方法,这个不是本次的讨论范围,当然你有兴趣下载源码去看下也行。你只要知道,这只是一个提供了对图片进行压缩以及缓存的方法就够了。这里有一点要注意,如果你不对图片进行压缩,那么你在设置图片到imageview上面的时候可能会有一点点卡顿,图片越大越明显,这是不能容忍的。
接着为item设置TouchListener,在按下屏幕down的时候纪录下手指在屏幕中的坐标,然后在move计算用户手指移动的绝对值,并且根据这个值动态设置item的透明度以及旋转角度,并且根据左滑或者右滑显示忽略图片或者感兴趣图片。然后再up的时候判断移动的距离是否超过了我们的界限值,如果超过了,删除该View,如果没超过,对item进行复原,也就是恢复到你按下手指时候的样子。
定义一个方法,用于删除view
public void removeView(final View view, boolean left){view.animate().alpha(0).rotation(left ? -90 : 90).setDuration(300).setListener(new AnimatorListenerAdapter(){@Overridepublic void onAnimationEnd(Animator animation){mContainer.removeView(view);if (mContainer.getChildCount() == 2)//如果只剩一条item和背景图片{//隐藏背景图片mContainer.getChildAt(0).setVisibility(View.GONE);} else if ((mContainer.getChildCount() == 1)){Toast.makeText(MainActivity.this, "已是最后一页...", Toast.LENGTH_SHORT).show();}}});}

这个方法看起来比上面那个添加view的代码舒心多了,因为它比较短,嗯,男人总喜欢看到比他短的。删除view非常简单,直接使用FrameLayout的removeView就可以了。不过这里我们不能直接remove,不然会显得非常突兀,我们应该给它设置一个动画,让其在你设置的时间内,透明度变化到0并且根据左滑还是右滑进行旋转,然后在动画结束的时候再进行remove。在item都remove完了时候,记得给用户个提示哦,不然显得非常的不友好。

呼,终于分析完了主界面代码了,接下来说一些小知识点。

多种形状的ImageView

关于这个,同样不是本次的讨论,知道这个的目测也不用我废话了,不知道的自己下载源码看或者网上搜搜吧,网上关于实现圆形图片的要多少有多少,我目前知道的有两种,一种是通过Xfermode,一种是通过BitmapShader,我这里用的是BitmapShader。圆形图片的喔就不说了,这里我给出实现那个下边带弧形的矩形图片的核心代码。
matrix = new Matrix();bitmapShader = new BitmapShader(src, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);matrix.setScale(scale, scale);bitmapShader.setLocalMatrix(matrix);paint.setShader(bitmapShader);

Path path = new Path();//贝塞尔曲线的操作点x yint deviation = 50;//圆弧突起的高度int controlY = getMeasuredHeight() + deviation;int controlX = getMeasuredWidth() / 2;path.moveTo(0, 0);path.lineTo(getMeasuredWidth(), 0);path.lineTo(getMeasuredWidth(), getMeasuredHeight() - deviation);path.quadTo(controlX, controlY, 0, getMeasuredHeight() - deviation);path.close();canvas.drawPath(path, paint);

以矩形左上角的点作为起点,弧线是利用贝塞尔曲线绘制的,有学过一点绘图软件的应该不难理解这个,没学过的也没关系,你只要知道,它可以通过起点、终点和操作点,画出一条弧线就好了,具体可以自己用用就知道了。

对ImageView的高斯模糊

虽然安卓有提供一个RenderScript框架可以实现高斯模糊的效果,不过表现不是特别好,在stackoverflow发现了一个更好的对图片进行模糊的类,具体算法太复杂,我们没必要理解,我们能使用就行。
public static Bitmap blur(Bitmap src, View view){float scaleFactor = 8;float radius = 5;Bitmap overlay = Bitmap.createBitmap((int) (view.getMeasuredWidth() / scaleFactor),(int) (view.getMeasuredHeight() / scaleFactor),Bitmap.Config.ARGB_8888);Canvas canvas = new Canvas(overlay);canvas.translate(-view.getLeft() / scaleFactor, view.getTop()/ scaleFactor);canvas.scale(1 / scaleFactor, 1 / scaleFactor);Paint paint = new Paint();paint.setFlags(Paint.FILTER_BITMAP_FLAG);canvas.drawBitmap(src, 0, 0, paint);overlay = FastBlur.doBlur(overlay, (int) radius, true);return overlay;}

直接复制项目文件的FastBlur.java类到你的项目中,然后复制以上代码,传入你要模糊的bitmap,以及你要用户显示bitmap的view就可以完成对bitmap的高斯模糊了。

结束

核心代码都已经贴完了,想看完整的可以通过我下面的链接down下来运行一下,最好是用AndroidStudio,比较方便,用eclipse的话就自己把代码复制过去吧,还有记得资源,都帮你们准备好拉。这效果你们满意了吗?显然没有,有点编程基础的肯定都能看出来,嗯,耦合性太高了,过几天有空我将抽取出一个单独的自定义布局来实现这种效果,以便有更好的复用性。

终于写完了我的第一篇博客,写的可能会有哪里表达的不好,各位前辈或者同辈有什么可以教我的都可以在下面留言哦。

下篇博客点击打开链接


代码地址:https://github.com/Chenantao/ImitateApp


转载注明出处




0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 练习瑜伽又?带硬怎么办 天气太热没有空调怎么办 腹股沟岔气怎么办才能快点好 瑜伽馆不给退卡怎么办 膝盖总是凉凉的怎么办 练阴瑜伽腿麻怎么办 瑜伽垫在地板滑怎么办 艾灸后后背发凉怎么办 床上老是有小蜈蚣怎么办 早晚出去胳膊脚觉得凉怎么办 腿凉感觉冒凉气怎么办 冬天腿被冻夏天发酸发凉怎么办 宿舍一楼虫子多怎么办 有虫子在咬床板怎么办 床板上的虫咬了怎么办 住的房间有臭虫怎么办 租的房子有臭虫怎么办 瑜伽垫放地上脏怎么办 瑜伽垫和地面滑怎么办 练瑜伽时瑜伽垫全是汗水怎么办 车钥匙放洗衣机洗了怎么办 瑜伽垫边上掉渣怎么办 晚上睡地上后背不舒服怎么办 小孩子天天晚上看电视不睡觉怎么办 宝宝天天晚上不睡觉怎么办 老公天天晚上不睡觉怎么办 小孩天天晚上不睡觉怎么办 摸了貔貅的眼睛怎么办 买的爬行垫滑怎么办 买了爬行垫有毒怎么办 xpe爬行垫破了怎么办 做瑜伽时平衡不好怎么办 鼻翼两侧发红长痘怎么办 孕后期睡觉背疼怎么办 练了瑜伽后腰痛怎么办 尿路口长疮疼痛怎么办 来月经吃了辣的怎么办 泳衣打湿后特别难脱怎么办 脚臭怎么办教你除臭方法 袜子没干就穿了进湿气怎么办 狗喜欢往床上跑怎么办