选开黑五人头像一人一张做头像

从一个搬运工到一个生产者是要一定时间的积累。。。
一个模仿探探头像编辑效果解析
此前一直在做模仿一个探探头像编辑的效果,但是水平不够一直没做出来,最后看到了丶亲一口就跑的源码
( ),恍然醒悟,对我的一些知识有全面提升。我觉得最主要的这个 AnimatorSet 不了解,当时一直在思考如何将多个动画同时执行在我的知识里就一个AnimationSet这个动画集合,但是做不到同时播放。因此没有做出这个效果。
好了不多说了开始随源码一起讲了。
先来看一下布局文件吧。
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:background="@color/white"
tools:context="com.szh.tantanphoto.ui.MainActivity" &
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" &
android:id="@+id/imageListView"
android:layout_width="match_parent"
android:layout_height="wrap_content" /&
android:id="@+id/Rootlayout"
android:layout_width="match_parent"
android:layout_height="match_parent" &
通过布局文件可以看出GridLayout和自定义的ViewGroup是有一部分是重合的。这个到代码后面GridLayout的作用就是让图片全屏拖动。
在他这个Demo整个的流程是开始在MainActivity里面执行oncreate开始实例化我们自定义的ViewGroup,在实例化ViewGroup的时候配置图片信息和初始化padding值
public AlbumView(Context context, AttributeSet attrs) {
super(context, attrs);
mImageOptions = ImageUtils.getFaceVideoOptions();
padding = dp2px(4, context);
然后在MainActivity的oncreate里面获取GridLayout并传如ViewGroup里面,并且传入一个图片对象集合进去,给ViewGroup设置其点击子控件事件。
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mAlbumView = getId(R.id.imageListView)
mAlbumView.setRootView((GridLayout) getId(R.id.Rootlayout))
mAlbumView.setImages(new DemoUtils().moarItems(6, getImageDate()))
mAlbumView.setOnItemClickListener(this)
将图片集合传进来后就开始往ViewGroup里面填充子控件,并设置Tag和触摸事件
* 初始化View集合,并向views里面添加数据
public void initUI() {
* 清空集合
views.clear();
* 清楚所有的view对象
removeAllViews();
for (int i = 0; i & images.size(); i++) {
ImageView view = new ImageView(getContext());
* 给ImageView设置填充父类布局的属性
view.setScaleType(ScaleType.FIT_XY);
if (!StringUtils.isEmpty(images.get(i).hyperlink)) {
mImageLoader.displayImage(images.get(i).hyperlink, view,
mImageOptions);
views.add(view);
addView(view);
initListener();
private void initListener() {
for (int i = 0; i & views.size(); i++) {
View view = views.get(i);
view.setTag(i);
view.setOnTouchListener(this);
然后Activity 执行生命周期开始画组件的视图调用onMeasure方法画布局了,在这里面进行动态计算ViewgGroup宽高并记录下来
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int resWidth = 0;
int resHeight = 0;
* 根据传入的参数,分别获取测量模式和测量值
int width = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
* 如果宽或者高的测量模式非精确值
if (widthMode != MeasureSpec.EXACTLY
|| heightMode != MeasureSpec.EXACTLY) {
* 主要设置为背景图的高度
resWidth = getSuggestedMinimumWidth();
* 如果未设置背景图片,则设置为屏幕宽高的默认值
resWidth = resWidth == 0 ? getDefaultWidth() : resW
resHeight = getSuggestedMinimumHeight();
* 如果未设置背景图片,则设置为屏幕宽高的默认值
resHeight = resHeight == 0 ? getDefaultWidth() : resH
* 如果都设置为精确值,则直接取小值;
resWidth = resHeight = Math.min(width, height);
setMeasuredDimension(resWidth, resHeight);
* 获得默认该layout的尺寸
private int getDefaultWidth() {
WindowManager wm = (WindowManager) getContext().getSystemService(
Context.WINDOW_SERVICE);
DisplayMetrics outMetrics = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(outMetrics);
return Math.min(outMetrics.widthPixels, outMetrics.heightPixels);
然后开始计算子控件的位置,并设置上。
protected void onLayout(boolean changed, int l, int t, int r, int b) {
* 获取父容器的宽度
int Width = getMeasuredWidth();
* 容器的宽度/3分-item之间的间隙
ItemWidth = Width / 3 - padding - (padding / 3);
System.out.println(l + "-" + t + "-" + r + "-" + b);
for (int i = 0, size = getChildCount(); i & i++) {
View view = getChildAt(i);
if (i == 0) {
mItmeOne = ItemWidth * 2 +
view.layout(l, t, l + mItmeOne, t + mItmeOne);
l += mItmeOne +
if (i == 1) {
view.layout(l, t, l + ItemWidth, t + ItemWidth);
t += ItemWidth +
if (i == 2) {
view.layout(l, t, l + ItemWidth, t + ItemWidth);
t += ItemWidth +
if (i &= 3) {
view.layout(l, t, l + ItemWidth, t + ItemWidth);
l -= ItemWidth +
* 如果当前绘制的view与拖动的view的是一样则让其隐藏
if (i == hidePosition) {
view.setVisibility(View.GONE);
mStartDragItemView =
好了原型布局是画好了,子控件的位置也是摆好了,就开始实现重头戏了,那就是触摸滑动了。
先是触摸缩小并移动到触摸的位置大家都值到关于触摸分发事件都会重写dispatchTouchEvent方法,如果对事件分发不熟悉的可以看guolin的《Android事件分发机制完全解析,带你从源码的角度彻底理解(上)》
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mHandler.removeCallbacks(mDragRunnable);
mDownX = (int) ev.getX();
mDownY = (int) ev.getY();
mDragPosition = pointToPosition(mDownX, mDownY);
* 判断获取的这个组件是否超出可以滑动的组件范围,如果超出了将分发事件
if (mDragPosition & maxSize) {
return super.dispatchTouchEvent(ev);
* 判断触摸的组件是否符合范围,不符合 将分发事件
if (mDragPosition == -1) {
return super.dispatchTouchEvent(ev);
* 根据position获取该item所对应的View
mStartDragItemView = getChildAt(mDragPosition);
* 设置不销毁此View的cach
mStartDragItemView.setDrawingCacheEnabled(true);
* 获取此View的BitMap对象
mDragBitmap = Bitmap.createBitmap(mStartDragItemView
.getDrawingCache());
* 销毁cache
mStartDragItemView.destroyDrawingCache();
dragPointX = mStartDragItemView.getWidth() / 2;
dragPointY = mStartDragItemView.getHeight() / 2;
dragOffsetX = (int) (ev.getRawX() - mDownX);
dragOffsetY = (int) (ev.getRawY() - mDownY);
* 将多线程加入消息队列并延迟50毫秒执行
mHandler.postDelayed(mDragRunnable, 50);
case MotionEvent.ACTION_MOVE:
moveX = (int) ev.getX();
moveY = (int) ev.getY();
if (mDragImageView != null) {
onDragItem(moveX - dragPointX + dragOffsetX, moveY - dragPointY
+ dragOffsetY - mTopHeight);
onSwapItem(moveX, moveY);
case MotionEvent.ACTION_UP:
onStopDrag();
mHandler.removeCallbacks(mDragRunnable);
case MotionEvent.ACTION_CANCEL:
onStopDrag();
mHandler.removeCallbacks(mDragRunnable);
return super.dispatchTouchEvent(ev);
这里面是做了些什么事呢?首先获取你收触摸的位置判断是否触摸在了ViewGrou的组件子控件上,并获取当前时间,如果触摸上了就且抬起的时间较短,那么调用item点击回调方法,如果时间长则进行获取触摸上的那个控件一个缓存图片并赋值给一个ImageView的引用,并设置到GridLayout上,并且进行缩小位移动画。
* 创建拖动的镜像
按下的点相对父控件的X坐标
按下的点相对父控件的X坐标
private void createDragImage() {
int[] location = new int[2];
mStartDragItemView.getLocationOnScreen(location);
float drX = location[0];
float drY = location[1] - mTopH
* 创建一个ImageView并将你点击的那一个item的Bitmap存进去
mDragImageView = new ImageView(getContext());
mDragImageView.setImageBitmap(mDragBitmap);
RootView.addView(mDragImageView);
int drH = (int) (ItemWidth * 0.8);
float w = mStartDragItemView.getWidth();
final float scale = drH /
createTranslationAnimations(mDragImageView, drX,
mDownX - dragPointX + dragOffsetX, drY,
mDownY - dragPointY + dragOffsetY - mTopHeight, scale, scale)
.setDuration(200).start();
* 缩放动画加平移动画
将要执行动画的View组件
开始时的X坐标
结束时的X坐标
开始时的Y坐标
结束时的Y坐标
X轴的缩放比例
Y轴的缩放比列
* 返回一个动画集合
private AnimatorSet createTranslationAnimations(View view, float startX,
float endX, float startY, float endY, float scaleX, float scaleY) {
AnimatorSet animSetXY = new AnimatorSet();
animSetXY.playTogether(ObjectAnimator.ofPropertyValuesHolder(view,
PropertyValuesHolder.ofFloat("translationX", startX, endX),
PropertyValuesHolder.ofFloat("translationY", startY, endY),
PropertyValuesHolder.ofFloat("scaleX", 1.0f, scaleX),
PropertyValuesHolder.ofFloat("scaleY", 1.0f, scaleY)));
return animSetXY;
然后就是拖着镜像进行移动的动画了
* 创建移动动画
动画执行的View
动画开始的X坐标
结束时的X坐标
开始时的Y坐标
结束时的Y坐标
* 返回一个动画集合
private AnimatorSet createTranslationAnimations(View view, float startX,
float endX, float startY, float endY) {
System.out.println("pppppppppppppppppppppppppppppppppppppp");
AnimatorSet animSetXY = new AnimatorSet();
animSetXY.playTogether(ObjectAnimator.ofPropertyValuesHolder(view,
PropertyValuesHolder.ofFloat("translationX", startX, endX),
PropertyValuesHolder.ofFloat("translationY", startY, endY)));
return animSetXY;
移动的时候记录了他移动的位置并且判断是否到了其他子控件的位置上了。如果在他上面了那么就开始真个ViewGroup的子控件移动。
ps 这个就是重点了。这个AnimationSet实现了动画效果。
* item的交换动画效果
* oldPosition
正在拖拽的那一个View的编号
* newPosition
当前触摸到的那个组件的编号
public void animateReorder(int oldPosition, int newPosition) {
* 判断触摸到的坐标的那一个View的编号是否大于现在正在拖拽的那一个坐标
boolean isForward = newPosition & oldP
final List&Animator& resultList = new LinkedList&Animator&();
if (isForward) {
for (int pos = oldPosition + 1; pos &= newP pos++) {
View view = getChildAt(pos);
if (pos == 1) {
float h = view.getWidth() / 2;
float mSpacing = padding / 2;
float w = getChildAt(0).getWidth();
float scale = w / view.getWidth();
resultList.add(createTranslationAnimations(view, 0,
-(view.getWidth() + padding + mSpacing + h), 0, h
+ mSpacing, scale, scale));
swap(images, pos, pos - 1);
if (pos == 2) {
resultList.add(createTranslationAnimations(view, 0, 0, 0,
-(view.getWidth() + padding)));
swap(images, pos, pos - 1);
if (pos == 3) {
resultList.add(createTranslationAnimations(view, 0, 0, 0,
-(view.getWidth() + padding)));
swap(images, pos, pos - 1);
if (pos == 4) {
resultList.add(createTranslationAnimations(view, 0,
view.getWidth() + padding, 0, 0));
swap(images, pos, pos - 1);
if (pos == 5) {
resultList.add(createTranslationAnimations(view, 0,
view.getWidth() + padding, 0, 0));
swap(images, pos, pos - 1);
for (int pos = newP pos & oldP pos++) {
View view = getChildAt(pos);
if (pos == 0) {
float h = getChildAt(1).getWidth() / 2;
float mSpacing = padding / 2;
float w = getChildAt(0).getWidth();
float scale = getChildAt(1).getWidth() /
resultList.add(createTranslationAnimations(view, 0,
getChildAt(1).getWidth() + padding + mSpacing + h,
0, -(h + mSpacing), scale, scale));
if (pos == 1) {
resultList.add(createTranslationAnimations(view, 0, 0, 0,
view.getWidth() + padding));
if (pos == 2) {
resultList.add(createTranslationAnimations(view, 0, 0, 0,
view.getWidth() + padding));
if (pos == 3) {
resultList.add(createTranslationAnimations(view, 0,
-(view.getWidth() + padding), 0, 0));
if (pos == 4) {
resultList.add(createTranslationAnimations(view, 0,
-(view.getWidth() + padding), 0, 0));
for (int i = oldP i & newP i--) {
swap(images, i, i - 1);
hidePosition = newP
resultSet = new AnimatorSet();
* 给动画填充动画集
resultSet.playTogether(resultList);
* 设置动画时间
resultSet.setDuration(150);
* 设置其播放模式
resultSet.setInterpolator(new OvershootInterpolator(1.6f));
resultSet.addListener(new AnimatorListenerAdapter() {
public void onAnimationStart(Animator animation) {
mAnimationEnd = false;
public void onAnimationEnd(Animator arg0) {
if (!mAnimationEnd) {
resultSet.removeAllListeners();
resultSet.clone();
resultSet = null;
mDragPosition = hideP
mAnimationEnd = true;
resultSet.start();
resultList.clear();
ok整个流程就是这样的了。具体细节去看我给加了注释的源码(很详细的哦*^__\^* )。
感谢丶亲一口就跑的源码( )
源码地址:
没有更多推荐了,点标签看更多好帖
更新于& 20:20
想选一张做头像!给点建议好吗?
显示方式:
TA共获得:
评分共:2 条
共获得金币 1
发表于 17:53
想选一张做头像!给点建议好吗?
第二张 最有活力
TA共获得:
评分共:0 条
发表于 18:51
想选一张做头像!给点建议好吗?
TA共获得:
评分共:0 条
发表于 19:27
想选一张做头像!给点建议好吗?
第三张,有结果。
TA共获得:
评分共:0 条
结婚后的子,像开车上路,像画家手中画笔,不用心,结果总会令人不满意。
发表于 10:52
TA共获得:
评分共:0 条
做什么事都要坚持,一步一个脚印,加油!!!
发表于 15:26
引用:1第二张 最有活力谢谢亲
TA共获得:
评分共:1 条
发表于 15:27
引用:1喜欢第2张谢谢亲
TA共获得:
评分共:0 条
发表于 15:27
引用:1第三张,有结果。哈哈谢谢
TA共获得:
评分共:0 条
发表于 15:27
引用:1第七张谢谢亲的建议
TA共获得:
评分共:0 条
发表于 15:28
按照大多数朋友的建议我暂时用第二张,谢谢你们
TA共获得:
评分共:0 条
发表于 15:40
TA共获得:
评分共:0 条
做什么事都要坚持,一步一个脚印,加油!!!
发表于 15:43
TA共获得:
评分共:0 条
发表于 17:26
引用:1第二谢谢啦
TA共获得:
评分共:0 条
发表于 22:51
TA共获得:
评分共:0 条
发表于 09:30
引用:1有沟必火
TA共获得:
评分共:0 条
使用(可批量传图、插入视频等)
Ctrl + Enter 快速发布
热门推荐:
&19楼百事通
违法和不良信息举报电话:9
举报邮箱:— — — — 不忘初心,方得始终 — — — —▼▼▼
一位来自韩国的自由插画师给自己取了一个中文绰号DADACHYO(达达踔)粉丝们叫她“达达”
她笔下的人物都超级美随便选一张都可以做头像
图文来源:网络 版权归原作者所有 · 禁止商用 美术名师
msmsnb(长按可复制)美术生
mssxcx(长按可复制)如 需 投 稿请 回 复“投 稿”微信:syjqqj(长按可复制)图文来源:互联网 版权归原作者所有·禁止商用点 击 阅 读 往 期 精 选
说真的,这手绘比照片震撼得多15岁辍学的80后卡车司机,死里逃生后拿起画笔正在认真画画的女生,真美!他坚持画画180天,画了1200颗珠子这就是美术生,自己看,说多了都是泪。往期回顾 谢谢分享
特别声明:本文为网易自媒体平台“网易号”作者上传并发布,仅代表该作者观点。网易仅提供信息发布平台。
一键安装官方客户端
重大事件及时推送 阅读更流畅
http://spider.nosdn.127.net/e0abb1f108ca01afe2dedd.jpeg趣味测试:选择一张喜欢的头像,测你的逻辑能力如何趣味测试:选择一张喜欢的头像,测你的逻辑能力如何大魔王喵喵百家号人在说话,做事时都会有自己的逻辑,若是你有一个优秀的逻辑推理能力,它能够让你在人群中绽放不一样的光芒,你会让别人感到望尘莫及。若是你缺乏逻辑推理能力,则会让人感觉你这个人有点呆头呆脑的。那么,你的逻辑能力如何?我们一起来看看吧!选择一张喜欢的头像答案解析:a:单行道式,只能单向地思考你思考模式属于单行道式,只能单向地思考,不能反向或是多向思考,所以很容易陷入一种刻板的模式当中,很少有创意性的突破。在日常生活中,一定是常常忘东忘西,想起了前面,后面的又疏忽了。在学习方面,你可能比较擅长需要记忆的科目,但在数学或其他需要逻辑推理的科目上,成绩可能较差一些。b:喜欢探究事物的本质你喜欢探究事物的本质,例如某些知识的来源和本质等等问题,这种人的思考模式属于溯源型,很多时候是从一个源点去思考整个事件,或者是寻找一个起点和中心点,以便理解整个事件,你最多的问题可能是:这句话出自哪里?这个概念为什么是这样?c:偏重归纳,注重和相信结果你思考模式偏重归纳,注重和相信结果,在汲取新的知识的同时,太过于依赖和相信现有的知识,缺乏追根溯源的精神。你的思维模式,比较适合学习需要吸取大量资料和知识性的科目,例如历史、地理、语文、英语等。d:心思缜密、有着很强的逻辑分析能力、判断能力心思缜密的你,有着很强的逻辑分析能力、判断能力。你会对每一处疑点都进行深度思考,并且根据手里所掌握的证据,大胆的假设,小心的验证。对于现场留下的一些不起眼的物证,你也能去伪存真,准确的做出判断。喜欢的记得给喵喵点个赞哦~更多资讯与分享请关注我哦!(此测试仅供娱乐)本文由百家号作者上传并发布,百家号仅提供信息发布平台。文章仅代表作者个人观点,不代表百度立场。未经作者许可,不得转载。大魔王喵喵百家号最近更新:简介:你错了!我不是忘记一切,我只是一无所有。作者最新文章相关文章49650 条评论分享收藏感谢收起赞同 15441 条评论分享收藏感谢收起

我要回帖

更多关于 张字头像 的文章

 

随机推荐