模仿半塘app的图片加入标签功能,刚開始反编译了半塘的代码,结果代码太多了,用一些三方的东西。觉的比較麻烦,这里自己写了一下实现。感觉和半塘的没啥差别(自我感觉良好,嘿嘿)
一、半塘功能实现步骤
二、半塘实现分析
功能分析
- 本地相冊选择
- 加入图片
- 加入标签
- 标签移动
- 标签动画切换
- 图片生成
实现说明
(1) 标签类型有两种 第一种是单个标签 另外一种是两个标签
(2) 第一种标签的包括:原点和文字
(3) 第二步标签包括:原点、文字和横线
(4) 原点的位置是点击的位置
(5) 文字有两种样式。一种是向左的,一种是向右的(相应实现)
(6) 横线有四种样式
三、实现
(1)类结构图
(2)代码结构
(3)详细实现
圆点的实现
public class LabelRoundDot extends Drawable{
private Paint mPaint;
private Bitmap mBitmap;
private RectF rectF;
public LabelRoundDot(Bitmap bitmap) {
mBitmap = bitmap;
BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setShader(bitmapShader);
}
@Override
public void setBounds(int left, int top, int right, int bottom)
{
super.setBounds(left, top, right, bottom);
rectF = new RectF(left, top, right, bottom);
}
@Override
public void draw(Canvas canvas) {
canvas.drawRoundRect(rectF, 30, 30, mPaint);
}
@Override
public int getIntrinsicWidth()
{
return mBitmap.getWidth();
}
@Override
public int getIntrinsicHeight()
{
return mBitmap.getHeight();
}
@Override
public void setAlpha(int alpha)
{
mPaint.setAlpha(alpha);
}
@Override
public void setColorFilter(ColorFilter cf)
{
mPaint.setColorFilter(cf);
}
@Override
public int getOpacity()
{
return PixelFormat.TRANSLUCENT;
}
}
文字的实现
初始化
public LabelTextView(String labelText, int textType){
this.labelText = labelText;
this.currentType = textType;
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setTextSize(TEXT_SIZE);
initLabel();
}
private void initLabel(){
Rect mBounds = new Rect();
mPaint.getTextBounds(this.labelText, 0, this.labelText.length(), mBounds);
this.labelTextHeight = mBounds.height() + VIEW_PADDING;
this.labelTextWidth = labelTextHeight/2 +(mBounds.width()<6? 6:mBounds.width()) + VIEW_PADDING;
Log.i(TAG, "initLabel labelTextHeight = " + labelTextHeight + " labelTextWidth = " + labelTextWidth);
}
画第一种类型
private void drawFrist(Canvas canvas){
mPaint.setStrokeWidth(3);
mPaint.setColor(Color.parseColor("#99000000"));
//画半圆
RectF oval = new RectF(0, 0, labelTextHeight, labelTextHeight);
canvas.drawArc(oval, 270, -180, true, mPaint);
Rect targetRect = new Rect(labelTextHeight/2, 0, labelTextWidth - VIEW_PADDING, labelTextHeight);
canvas.drawRect(targetRect, mPaint);
//画三角形
Path path = new Path();
path.moveTo(labelTextWidth - VIEW_PADDING, 0);// 此点为多边形的起点
path.lineTo(labelTextWidth, labelTextHeight/2);
path.lineTo(labelTextWidth - VIEW_PADDING, labelTextHeight);
path.close(); // 使这些点构成封闭的多边形
canvas.drawPath(path, mPaint);
//写文字
mPaint.setColor(Color.parseColor("#ffffff"));
Paint.FontMetricsInt fontMetrics = mPaint.getFontMetricsInt();
int baseline = (targetRect.bottom + targetRect.top - fontMetrics.bottom - fontMetrics.top) / 2;
// 以下这行是实现水平居中,drawText相应改为传入targetRect.centerX()
mPaint.setTextAlign(Paint.Align.CENTER);
canvas.drawText(labelText, targetRect.centerX(), baseline, mPaint);
}
画另外一种类型
private void drawSecond(Canvas canvas){
Log.i(TAG, "onDrawSecond ---------");
mPaint.setStrokeWidth(3);
mPaint.setColor(Color.parseColor("#99000000"));
//画半圆
RectF oval = new RectF(labelTextWidth - labelTextHeight, 0, labelTextWidth, labelTextHeight);
canvas.drawArc(oval,90, -180, true, mPaint);
//画矩形
Rect targetRect = new Rect(VIEW_PADDING, 0, labelTextWidth-labelTextHeight/2, labelTextHeight);
canvas.drawRect(targetRect, mPaint);
//画三角形
Path path = new Path();
path.moveTo(VIEW_PADDING, 0);// 此点为多边形的起点
path.lineTo(0, labelTextHeight/2);
path.lineTo(VIEW_PADDING, labelTextHeight);
path.close(); // 使这些点构成封闭的多边形
canvas.drawPath(path, mPaint);
//写文字
mPaint.setColor(Color.parseColor("#ffffff"));
Paint.FontMetricsInt fontMetrics = mPaint.getFontMetricsInt();
int baseline = (targetRect.bottom + targetRect.top - fontMetrics.bottom - fontMetrics.top) / 2;
// 以下这行是实现水平居中,drawText相应改为传入targetRect.centerX()
mPaint.setTextAlign(Paint.Align.CENTER);
canvas.drawText(labelText, targetRect.centerX(), baseline, mPaint);
}
线条的实现
初始化
public LabelLineView (int type){
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setStrokeWidth(3);
mPaint.setColor(Color.parseColor("#99000000"));
labelHeight = slashHeight;
labelWidth = slashWidth + straightLine;
this.currentType = type;
Log.i(TAG, "onDrawFirst labelHeight = " + labelHeight + " labelWidth = " + labelWidth);
}
四种类型
/**
*
* @param canvas
*/
private void drawFrist(Canvas canvas){
//画第一条直线
int lineSx = 0;
int lineSy = 0;
int lineEx = straightLine;
int lineEy = 0;
canvas.drawLine(lineSx, lineSy, lineEx, lineEy, mPaint);// 画线
lineSx = straightLine;
lineSy = 0;
lineEx = labelWidth;
lineEy = labelHeight;
canvas.drawLine(lineSx, lineSy, lineEx, lineEy, mPaint);// 画线
}
/**
*
* @param canvas
*/
private void drawSecond(Canvas canvas){
//画第一条直线
int lineSx = 0;
int lineSy = labelHeight;
int lineEx = straightLine;
int lineEy = labelHeight;
canvas.drawLine(lineSx, lineSy, lineEx, lineEy, mPaint);// 画线
lineSx = straightLine;
lineSy = labelHeight;
lineEx = labelWidth;
lineEy = 0;
canvas.drawLine(lineSx, lineSy, lineEx, lineEy, mPaint);// 画线
}
private void drawThird(Canvas canvas){
int lineSx = 0;
int lineSy = labelHeight;
int lineEx = slashWidth;
int lineEy = 0;
canvas.drawLine(lineSx, lineSy, lineEx, lineEy, mPaint);// 画线
lineSx = slashWidth;
lineSy = 0;
lineEx = labelWidth;
lineEy = 0;
canvas.drawLine(lineSx, lineSy, lineEx, lineEy, mPaint);// 画线
}
private void drawFourth(Canvas canvas){
//画第一条直线
int lineSx = 0;
int lineSy = 0;
int lineEx = slashWidth;
int lineEy = slashHeight;
canvas.drawLine(lineSx, lineSy, lineEx, lineEy, mPaint);// 画线
lineSx = slashWidth;
lineSy = slashHeight;
lineEx = labelWidth;
lineEy = labelHeight;
canvas.drawLine(lineSx, lineSy, lineEx, lineEy, mPaint);// 画线
}
LabelViewListener标签的实现监听
包括:标签的位置、切换样式、显示和隐藏动画、更新坐标(中心)、更新内容、推断是否点击到中心等方法。
仅仅有一个内容的标签
有两种绘制模式。包括原点和文字,记录中心位置和当前的模式
包括绘制两种模式的方法、显示和隐藏的动画、提供view的大小和位置等。
第一种模式
/**
* 绘制第一模式
*
* @param canvas
*/
private void onDrawFirst(Canvas canvas) {
Log.i(TAG, "onDrawFirst ---------");
canvas.drawBitmap(Utils.drawableToBitmap(labelTextView),4,0,mPaint);
//画原点
int xBmp = labelViewWidth-originPic.getWidth();
int yBmp = (labelViewHeight-originPic.getWidth())/2;
canvas.drawBitmap(originPic, xBmp, yBmp, null);
Log.i(TAG, "onDrawFirst xBmp = " + xBmp + " yBmp = " + yBmp);
}
另外一种模式
/**
* 绘制第二模式
*
* @param canvas
*/
private void onDrawSecond(Canvas canvas) {
Log.i(TAG, "onDrawSecond ---------");
canvas.drawBitmap(Utils.drawableToBitmap(labelTextView), originPic.getWidth()-4, 0, mPaint);
//画原点
int xBmp = 0;
int yBmp = (labelViewHeight-originPic.getWidth())/2;
canvas.drawBitmap(originPic, xBmp, yBmp, null);
Log.i(TAG, "onDrawFirst xBmp = " + xBmp + " yBmp = " + yBmp);
}
显示动画
@Override
public ValueAnimator getLabelShowAnim() {
ValueAnimator anim = null;
if (currentType == FRIST_MODEL) {
anim = ValueAnimator.ofInt(labelViewWidth, 0);
} else if (currentType == SECOND_MODEL) {
anim = ValueAnimator.ofInt(0, labelViewWidth);
}
if (anim == null) {
return null;
}
anim.setDuration(500);
return anim;
}
隐藏动画
@Override
public ValueAnimator getLabelHideAnim() {
ValueAnimator anim = null;
if (currentType == FRIST_MODEL) {
anim = ValueAnimator.ofInt(0, labelViewWidth);
} else if (currentType == SECOND_MODEL) {
anim = ValueAnimator.ofInt(labelViewWidth, 0);
}
if (anim == null) {
return null;
}
anim.setDuration(500);
return anim;
}
有两个内容的标签
包括圆点、线条和文字
有四种绘制模式
第一种模式
/**
* 绘制第一模式
*
* @param canvas
*/
private void onDrawFirst(Canvas canvas) {
Log.i(TAG, "onDrawFirst ---------");
//上面的文字
canvas.drawBitmap(Utils.drawableToBitmap(labelTextViewFirst),
labelViewWidth-originPic.getWidth()-labelLineViewFirst.getIntrinsicWidth()-labelTextViewFirst.getIntrinsicWidth(),
0,mPaint);
//上面的线条
canvas.drawBitmap(Utils.drawableToBitmap(labelLineViewFirst),
labelViewWidth-originPic.getWidth()-labelLineViewFirst.getIntrinsicWidth(),
labelTextViewFirst.getIntrinsicHeight()/2,
mPaint);
//以下的文字
canvas.drawBitmap(Utils.drawableToBitmap(labelTextViewSecond),
labelViewWidth - originPic.getWidth() - labelLineViewSecond.getIntrinsicWidth() - labelTextViewSecond.getIntrinsicWidth(),
labelViewHeight - labelTextViewSecond.getIntrinsicHeight(),
mPaint);
//以下的线条
canvas.drawBitmap(Utils.drawableToBitmap(labelLineViewSecond),
labelViewWidth - originPic.getWidth() - labelLineViewSecond.getIntrinsicWidth(),
labelTextViewFirst.getIntrinsicHeight() / 2 + labelLineViewFirst.getIntrinsicHeight(),
mPaint);
//画原点
int xBmp = labelViewWidth - originPic.getWidth();
int yBmp = (labelViewHeight - originPic.getWidth()) / 2;
canvas.drawBitmap(originPic, xBmp, yBmp, null);
Log.i(TAG, "onDrawFirst xBmp = " + xBmp + " yBmp = " + yBmp);
}
另外一种模式
/**
* 绘制第二模式
*
* @param canvas
*/
private void onDrawSecond(Canvas canvas) {
Log.i(TAG, "onDrawSecond ---------");
//上面条线
canvas.drawBitmap(Utils.drawableToBitmap(labelLineViewFirst),
originPic.getWidth(),
labelTextViewFirst.getIntrinsicHeight()/2,
mPaint);
//上面文字
canvas.drawBitmap(Utils.drawableToBitmap(labelTextViewFirst),
originPic.getWidth()+labelLineViewFirst.getIntrinsicWidth(),
0,
mPaint);
//以下线条
canvas.drawBitmap(Utils.drawableToBitmap(labelLineViewSecond),
originPic.getWidth(),
labelTextViewFirst.getIntrinsicHeight()/2+labelLineViewFirst.getIntrinsicHeight(),
mPaint);
//以下文字
canvas.drawBitmap(Utils.drawableToBitmap(labelTextViewSecond),
originPic.getWidth()+labelLineViewSecond.getIntrinsicWidth(),
labelViewHeight - labelTextViewSecond.getIntrinsicHeight(),
mPaint);
//画原点
int xBmp = 0;
int yBmp = (labelViewHeight-originPic.getWidth())/2;
canvas.drawBitmap(originPic, xBmp, yBmp, null);
Log.i(TAG, "onDrawSecond xBmp = " + xBmp + " yBmp = " + yBmp);
}
另外一种模式
/**
* 第三种绘制
*/
private void onDrawThird(Canvas canvas){
Log.i(TAG, "onDrawSecond ---------");
//左边的文字
canvas.drawBitmap(Utils.drawableToBitmap(labelTextViewFirst),
0,
0,
mPaint);
//上面条线
canvas.drawBitmap(Utils.drawableToBitmap(labelLineViewFirst),
labelTextViewFirst.getIntrinsicWidth(),
labelTextViewFirst.getIntrinsicHeight()/2,
mPaint);
//画原点
int xBmp = labelTextViewFirst.getIntrinsicWidth()+labelLineViewFirst.getIntrinsicWidth();
int yBmp = (labelViewHeight-originPic.getHeight())/2;
canvas.drawBitmap(originPic, xBmp, yBmp, null);
Log.i(TAG, "onDrawSecond xBmp = " + xBmp + " yBmp = " + yBmp);
//以下线条
canvas.drawBitmap(Utils.drawableToBitmap(labelLineViewSecond),
labelTextViewFirst.getIntrinsicWidth()+labelLineViewFirst.getIntrinsicWidth()+originPic.getWidth(),
labelViewHeight/2,
mPaint);
//以下文字
canvas.drawBitmap(Utils.drawableToBitmap(labelTextViewSecond),
labelViewWidth - labelTextViewSecond.getIntrinsicWidth(),
labelViewHeight - labelTextViewSecond.getIntrinsicHeight(),
mPaint);
}
第四种模式
/**
* 第四种绘制
*/
private void onDrawFourth(Canvas canvas){
Log.i(TAG, "onDrawFourth ---------");
//左边文字
canvas.drawBitmap(Utils.drawableToBitmap(labelTextViewFirst),
0,
labelViewHeight-labelTextViewFirst.getIntrinsicHeight(),
mPaint);
//左边条线
canvas.drawBitmap(Utils.drawableToBitmap(labelLineViewFirst),
labelTextViewFirst.getIntrinsicWidth(),
labelViewHeight-labelTextViewFirst.getIntrinsicHeight()/2-labelLineViewFirst.getIntrinsicHeight(),
mPaint);
//画原点
int xBmp = labelTextViewFirst.getIntrinsicWidth()+labelLineViewFirst.getIntrinsicWidth();
int yBmp = (labelViewHeight-originPic.getWidth())/2;
canvas.drawBitmap(originPic, xBmp, yBmp, null);
Log.i(TAG, "onDrawSecond xBmp = " + xBmp + " yBmp = " + yBmp);
//右边线条
canvas.drawBitmap(Utils.drawableToBitmap(labelLineViewSecond),
labelTextViewFirst.getIntrinsicWidth()+labelLineViewFirst.getIntrinsicWidth()+originPic.getWidth(),
labelTextViewSecond.getIntrinsicHeight()/2,
mPaint);
//右边文字
canvas.drawBitmap(Utils.drawableToBitmap(labelTextViewSecond),
labelViewWidth-labelTextViewSecond.getIntrinsicWidth(),
0,
mPaint);
}
以上大致说了一下思路
这是代码链接
https://github.com/gyh/GCustomView