看了Android版QQ的自定义头像功能,决定自己实现,随便熟悉下android绘制和图片处理这一块的知识。

先看看效果:

思路分析:

这个效果可以用两个View来完成,上层View是一个遮盖物,绘制半透明的颜色,中间挖了一个圆;下层的View用来显示图片,具备移动和缩放的功能,并且能截取某区域内的图片。

涉及到的知识点:

1.Matrix,图片的移动和缩放

2.Paint的setXfermode方法

3.图片放大移动后,截取一部分

编码实现:

自定义三个View:

1.下层View:ClipPhotoView

2.上层遮盖View:ClipPhotoCircleView

3.布局文件:ClipPhotoLayout,实现两层View的布局,且作为整个功能的facade

ClipPhotoCircleView代码:

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

drawMask(canvas);

}

/**

* 绘制蒙版

*/

private void drawMask(Canvas canvas) {

//画背景颜色

Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);

Canvas c1 = new Canvas(bitmap);

c1.drawARGB(150, 0, 0, 0);

Paint strokePaint = new Paint();

strokePaint.setAntiAlias(true);

strokePaint.setColor(Color.WHITE);

strokePaint.setStyle(Paint.Style.STROKE);

strokePaint.setStrokeWidth(STROKE_WIDTH);

c1.drawCircle(getWidth() / 2, getHeight() / 2, getRadius(), strokePaint);

//画圆

Bitmap circleBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);

Canvas c2 = new Canvas(circleBitmap);

Paint circlePaint = new Paint();

circlePaint.setStyle(Paint.Style.FILL);

circlePaint.setColor(Color.RED);

circlePaint.setAntiAlias(true);

c2.drawCircle(getWidth() / 2, getHeight() / 2, getRadius(), circlePaint);

//两个图层合成

Paint paint = new Paint();

paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));

c1.drawBitmap(circleBitmap, 0, 0, paint);

paint.setXfermode(null);

canvas.drawBitmap(bitmap, 0, 0, null);

}

使用了setXfermode,Mode为DST_OUT,如下图:

ClipPhotoView代码:

/**

* Created by caocong on 10/9/16.

* 显示图片的view,可以托动和缩放

*/

public class ClipPhotoView extends ImageView implements View.OnTouchListener,

ScaleGestureDetector.OnScaleGestureListener {

private static final String TAG = ClipPhotoView.class.getSimpleName();

//最大缩放比例

private static final float MAX_SCALE = 4.0f;

//最小缩放比例

private static float MIN_SCALE = 1.0f;

//matrix array

private static final float MATRIX_ARR[] = new float[9];

/**

* 状态

*/

private static final class Mode {

// 初始状态

private static final int NONE = 0;

//托动

private static final int DRAG = 1;

//缩放

private static final int ZOOM = 2;

}

//当前状态

private int mMode = Mode.NONE;

//缩放手势

private ScaleGestureDetector mScaleDetector;

//矩阵

private Matrix mMatrix = new Matrix();

//托动时手指按下的点

private PointF mPrevPointF = new PointF();

//截取的圆框的半径

private int mRadius;

//第一次

private boolean firstTime = true;

public ClipPhotoView(Context context) {

this(context, null);

}

public ClipPhotoView(Context context, AttributeSet attrs) {

this(context, attrs, 0);

}

public ClipPhotoView(Context context, AttributeSet attrs, int defStyleAttr) {

super(context, attrs, defStyleAttr);

mScaleDetector = new ScaleGestureDetector(context, this);

mRadius = Util.getRadius(getContext());

// 必须设置才能触发

setOnTouchListener(this);

setScaleType(ScaleType.MATRIX);

}

/**

* 初始化

*/

private void init() {

Drawable drawable = getDrawable();

if (drawable == null) {

//throw new IllegalArgumentException("drawable can not be null");

return;

}

initPosAndScale();

}

/**

* 初始化缩放比例

*/

private void initPosAndScale() {

if (firstTime) {

Drawable drawable = getDrawable();

int width = getWidth();

int height = getHeight();

//初始化

int dw = drawable.getIntrinsicWidth();

int dh = drawable.getIntrinsicHeight();

float scaleX = 1.0f;

float scaleY = 1.0f;

//是否已经做过缩放处理

boolean isScaled = false;

if (width < getDiameter()) {

scaleX = getDiameter() * 1.0f / width;

isScaled = true;

}

if (height < getDiameter()) {

scaleY = getDiameter() * 1.0f / height;

isScaled = true;

}

float scale = Math.max(scaleX, scaleY);

if (isScaled) {

MIN_SCALE = scale;

} else {

MIN_SCALE = Math.max((getDiameter() * 1.0f) / dw, getDiameter() * 1.0f / dh) + 0.01f;

}

Log.d(TAG, "scale=" + scale);

mMatrix.postScale(scale, scale, getWidth() / 2, getHeight() / 2);

mMatrix.postTranslate((width - dw) / 2, (height - dh) / 2);

setImageMatrix(mMatrix);

firstTime = false;

}

}

@Override

public boolean onScale(ScaleGestureDetector detector) {

float scale = getScale();

float scaleFactor = detector.getScaleFactor();

if ((scale >= MIN_SCALE && scaleFactor > 1.0f) ||

(scale <= MAX_SCALE && scaleFactor < 1.0f)) {

if (scale * scaleFactor <= MIN_SCALE) {

scaleFactor = MIN_SCALE / scale;

} else if (scale * scaleFactor >= MAX_SCALE) {

scaleFactor = MAX_SCALE / scale;

}

mMatrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusY());

checkTrans();

setImageMatrix(mMatrix);

}

return true;

}

@Override

public boolean onScaleBegin(ScaleGestureDetector detector) {

mMode = Mode.ZOOM;

return true;

}

@Override

public void onScaleEnd(ScaleGestureDetector detector) {

mMode = Mode.NONE;

}

@Override

public boolean onTouch(View v, MotionEvent event) {

if (getDrawable() == null) {

return false;

}

mScaleDetector.onTouchEvent(event);

switch (event.getAction() & MotionEvent.ACTION_MASK) {

case MotionEvent.ACTION_DOWN:

mMode = Mode.DRAG;

mPrevPointF.set(event.getX(), event.getY());

break;

case MotionEvent.ACTION_UP:

mMode = Mode.NONE;

break;

case MotionEvent.ACTION_MOVE:

if (mMode == Mode.DRAG && event.getPointerCount() == 1) {

float x = event.getX();

float y = event.getY();

float dx = event.getX() - mPrevPointF.x;

float dy = event.getY() - mPrevPointF.y;

RectF rectF = getMatrixRectF();

// 如果宽度小于屏幕宽度,则禁止左右移动

if (rectF.width() <= getDiameter()) {

dx = 0;

}

// 如果高度小雨屏幕高度,则禁止上下移动

if (rectF.height() <= getDiameter()) {

dy = 0;

}

mMatrix.postTranslate(dx, dy);

checkTrans();

//边界判断

setImageMatrix(mMatrix);

mPrevPointF.set(x, y);

}

break;

}

return true;

}

/**

* 移动边界检查

*/

private void checkTrans() {

RectF rect = getMatrixRectF();

float deltaX = 0;

float deltaY = 0;

int width = getWidth();

int height = getHeight();

int horizontalPadding = (width - getDiameter()) / 2;

int verticalPadding = (height - getDiameter()) / 2;

// 如果宽或高大于屏幕,则控制范围 ; 这里的0.001是因为精度丢失会产生问题

if (rect.width() + 0.01 >= getDiameter()) {

if (rect.left > horizontalPadding) {

deltaX = -rect.left + horizontalPadding;

}

if (rect.right < width - horizontalPadding) {

deltaX = width - horizontalPadding - rect.right;

}

}

if (rect.height() + 0.01 >= getDiameter()) {

if (rect.top > verticalPadding) {

deltaY = -rect.top + verticalPadding;

}

if (rect.bottom < height - verticalPadding) {

deltaY = height - verticalPadding - rect.bottom;

}

}

mMatrix.postTranslate(deltaX, deltaY);

}

/**

* 得到直径

*/

public int getDiameter() {

return mRadius * 2;

}

/**

* 获得缩放值

*

* @return

*/

private float getScale() {

return getMatrixValue(Matrix.MSCALE_X);

}

private float getMatrixValue(int index) {

mMatrix.getValues(MATRIX_ARR);

return MATRIX_ARR[index];

}

/**

* 获得Matrix的RectF

*/

private RectF getMatrixRectF() {

Matrix matrix = mMatrix;

RectF rect = new RectF();

Drawable d = getDrawable();

if (null != d) {

rect.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());

matrix.mapRect(rect);

}

return rect;

}

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

init();

}

/**

* 截取图片

*

* @return

*/

Bitmap clip() {

Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);

Canvas canvas = new Canvas(bitmap);

draw(canvas);

int x = (getWidth() - getDiameter()) / 2;

int y = (getHeight() - getDiameter()) / 2;

return Bitmap.createBitmap(bitmap, x, y, getDiameter(), getDiameter());

}

}

缩放和移动使用了Matrix的方法postScale()和postTranslate,要注意控制边界。

截图的代码在clip()方法中,原理:新建一个空白Bitmap,和屏幕一样大的尺寸,然后将当前View绘制的内容复制到到这个Bitmap中,然后截取该Bitmap的一部分。

ClipPhotoLayout代码:

public class ClipPhotoLayout extends FrameLayout {

private ClipPhotoCircleView mCircleView;

private ClipPhotoView mPhotoView;

public ClipPhotoLayout(Context context) {

this(context, null);

}

public ClipPhotoLayout(Context context, AttributeSet attrs) {

this(context, attrs, 0);

}

public ClipPhotoLayout(Context context, AttributeSet attrs, int defStyleAttr) {

super(context, attrs, defStyleAttr);

init();

}

private void init() {

mCircleView = new ClipPhotoCircleView(getContext());

mPhotoView = new ClipPhotoView(getContext());

android.view.ViewGroup.LayoutParams lp = new LinearLayout.LayoutParams(

android.view.ViewGroup.LayoutParams.MATCH_PARENT,

android.view.ViewGroup.LayoutParams.MATCH_PARENT);

addView(mPhotoView, lp);

addView(mCircleView, lp);

}

public void setImageDrawable(Drawable drawable) {

mPhotoView.setImageDrawable(drawable);

}

public void setImageDrawable(int resId) {

setImageDrawable(getContext().getDrawable(resId));

}

public Bitmap clipBitmap() {

return mPhotoView.clip();

}

}

测试MainActivity:

public class MainActivity extends Activity {

private ClipPhotoLayout mClipPhotoLayout;

private int[] pictures = {R.drawable.mingren, R.drawable.cute, R.drawable.tuxi};

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.scale);

setTitle("移动和缩放");

mClipPhotoLayout = (ClipPhotoLayout) findViewById(R.id.clip_layout);

mClipPhotoLayout.setImageDrawable(pictures[0]);

}

public void doClick(View view) {

Bitmap bitmap = mClipPhotoLayout.clipBitmap();

Intent intent = new Intent(this, ResultActivity.class);

intent.putExtra("photo", bitmap);

startActivity(intent);

}

}

MainActivity的布局文件:

android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical">

android:id="@+id/clip_layout"

android:layout_width="match_parent"

android:layout_height="0dp"

android:layout_weight="1.0"/>

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:onClick="doClick"

android:text="clip" />

以上所述是小编给大家介绍的Android 仿QQ头像自定义截取功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对脚本之家网站的支持!

android人脸识显示头像自定义,Android 仿QQ头像自定义截取功能相关推荐

  1. iOS自定义裁剪区域,正方形圆形图片头像裁剪,仿QQ头像裁剪,圆形遮罩,矩型遮罩

    最近项目中用到了自定义图片裁剪区域的图片裁剪功能,自己写了一个,可能有诸多不完善的地方,请大家指正. 支持任意区域裁剪,9:16裁剪.16:9裁剪.1:1裁剪.圆形裁剪等等,总之裁剪框的大小,裁剪框的 ...

  2. android wear支付宝6,Android自定义View仿支付宝输入六位密码功能

    跟选择银行卡界面类似,也是用一个PopupWindow,不过输入密码界面是一个自定义view,当输入六位密码完成后用回调在Activity中获取到输入的密码并以Toast显示密码.效果图如下: 自定义 ...

  3. Android仿支付宝UI功能开发,Android 自定义view仿支付宝咻一咻功能

    支付宝上有一个咻一咻的功能,就是点击图片后四周有水波纹的这种效果,今天也写一个类似的功能. 效果如下所示: 思路: 就是几个圆的半径不断在变大,这个可以使用动画缩放实现,还有透明动画 还有就是这是好几 ...

  4. android 高仿qq,Android高仿QQ头像截取

    花费了半天时间,把 仿QQ头像截取的方法整理了下,并制作了一个demo以供大家参考,基本上实现了qq中我的资料界面上(包括背景透明化,上滑标题栏显示,下拉隐藏等)的大致效果,先上图看效果吧: 支持的功 ...

  5. Android:高仿QQ头像截取

    花费了半天时间,把 仿QQ头像截取的方法整理了下,并制作了一个demo以供大家参考,基本上实现了qq中我的资料界面上(包括背景透明化,上滑标题栏显示,下拉隐藏等)的大致效果,先上图看效果吧: 支持的功 ...

  6. android的动态tab,Android自定义view仿QQ的Tab按钮动画效果(示例代码)

    话不多说 先上效果图 实现其实很简单,先用两张图 一张是背景的图,一张是笑脸的图片,笑脸的图片是白色,可能看不出来.实现思路:主要是再触摸view的时候同时移动这两个图片,但是移动的距离不一样,造成的 ...

  7. Android:高仿QQ头像截取升级版

    观看此篇文章前,请先阅读上篇文章:高仿QQ头像截取: 本篇之所以为升级版,是在截取头像界面添加了与qq类似的阴影层(裁剪区域以外的部分),且看效果图:   为了适应大家不同需求,这次打了两个包,及上图 ...

  8. Android特效专辑(十二)——仿支付宝咻一咻功能实现波纹扩散特效,精细小巧的View...

    Android特效专辑(十二)--仿支付宝咻一咻功能实现波纹扩散特效,精细小巧的View 先来看看这个效果 这是我的在Only上添加的效果,说实话,Only现在都还只是半成品,台面都上不了,怪自己技术 ...

  9. Android特效专辑(十二)——仿支付宝咻一咻功能实现波纹扩散特效,精细小巧的View

    Android特效专辑(十二)--仿支付宝咻一咻功能实现波纹扩散特效,精细小巧的View 先来看看这个效果 这是我的在Only上添加的效果,说实话,Only现在都还只是半成品,台面都上不了,怪自己技术 ...

最新文章

  1. Mongodb副本集--Out of memory: Kill process 37325 (mongod)
  2. Java Error(三)
  3. [Swift] 使用Playground
  4. 计算机在学前教育和美术绘画中的应用,幼儿园美术教学活动中信息技术的应用...
  5. 如何将函数的实际参数转换成数组
  6. 机械制造工艺基础_机械制造工艺基础知识,錾削与锯削加工工艺
  7. jquery 特效专辑
  8. 一步一步学Silverlight 2系列(5):实现简单的拖放功能_转载
  9. IOS 杂笔-14(被人遗忘的owner)
  10. Linux中创建一个不能登录的用户useradd
  11. HTML5七夕情人节表白网页制作【满天星空3D相册】HTML+CSS+JavaScript 3D动态相册网页代码
  12. 自我鉴定计算机专业大学,大学生计算机专业的自我鉴定书
  13. html网页有内容不能向下拉,为什么百度页面不能往下拉
  14. Wifidog扫盲篇
  15. 蓝桥冲刺31天打卡—Day14
  16. 工厂如何实现无线wifi短信验证登录?工厂上网实名认证系统
  17. 一文读懂大数据两大核心技术
  18. 【数据结构】单链表(增、删、查、改)的实现 [初阶篇_ 复习专用]
  19. 数字IC-1.10 手撕代码之整数乘法和二范数(Verilog HDL数字加减法练习好帮手)
  20. python 数据、曲线平滑处理

热门文章

  1. 知识太多,大脑不够用
  2. word设置的密码忘了怎么办?
  3. 格兰因果模型可以分析哪些东西_结构方程模型(SEM)和分段结构方程模型
  4. Power BI中使用DAX生动展现人员头像、动态标签——销售数据里的那些商业智能
  5. 2022-5-16作业
  6. 【云开发案例】网络安全技能提升知识竞赛答题活动小程序
  7. 主动模式和被动模式客户端
  8. 因用 ChatGPT 打官司,从业 30 年的律师反遭制裁:6 个案例全是假的!
  9. 新萝卜家园GHOST WIN7系统32,64位官方版下载
  10. 清华大学计算机系比赛,清华大学计算机系本科生于纪平获评中国大学生年度人物...