「这是我参与11月更文挑战的第14天,活动详情查看:2021最后一次更文挑战」
Android 可拖动的文字
目前市面上关于图片编辑的app很多,无论是截图还是专业的图片处理的app,很多都会有添加标签,添加贴纸的功能,笔者在也曾经遇到过几次编辑图片的需求,都不是很专业的图片处理软件,所以花花时间,总能磨出来。该篇文章将讲解如何实现文字的可拖动的效果,并限制它的边界,延伸它的功能。
需求
-
添加可以自定义的文字
-
文字可以删除,可以替换
-
文字需要限制边界
-
文字可以设置字号
-
文字需要可以设置对齐方式
-
文字无需旋转(并非专业的图片处理app)
简单的实现
先看下效果图
实际上所有的效果的代码逻辑非常清晰,只有自定义的View。那么来看看怎么实现的。
1. BaseStickerView类
定义 BaseStickerView 类并继承 ViewGroup,会默认需要创建一个构造方法
public BaseStickerView(Context context) {
super(context);
onInitialize(context);
}
复制代码
在该类中声明贴纸内容、可操作的按钮、帮助类、和缩放比例等属性
/* 贴纸内容 */
private View mContentView;
/* 可操作的按钮 */
private ImageView mRemoveView, mEditView;
/* 帮助类 */
private StickerMoveHelper mMoveHelper;
/* 默认缩放比 */
private float mScale = 1f;
复制代码
在构造方法中进行初始化视图。我们在初始化中,设置背景透明,将贴纸内容添加,初始化可操作的按钮,将可操作的按钮添加到视图。在这个方法中,我们还要初始化帮助类,主要的拖动方法在这个帮助类中进行,这样可以让代码结构更清晰一些。
public void onInitialize(Context context) {
// 背景透明
setBackgroundColor(Color.TRANSPARENT);
// 抽象 创建贴纸的视图,并添加
mContentView = onCreateContentView(context);
if (mContentView == null) return;
addView(mContentView, getContentLayoutParams());
// 创建可操作的图标
mRemoveView = new ImageView(context);
mRemoveView.setScaleType(ImageView.ScaleType.FIT_XY);
mRemoveView.setImageResource(R.drawable.sticker_ic_delete);
mEditView = new ImageView(context);
mEditView.setScaleType(ImageView.ScaleType.FIT_XY);
mEditView.setImageResource(R.drawable.sticker_ic_edit);
mRemoveView.setOnClickListener(this);
mEditView.setOnClickListener(this);
// 将组件view添加到视图
addView(mRemoveView, getAnchorLayoutParams());
addView(mEditView, getAnchorLayoutParams());
// 初始化这个帮助类
mMoveHelper = new StickerMoveHelper(this);
}
复制代码
我们在OnTouch的回调方法中,将事件传递给帮助类,让帮助类去帮我们实现滑动的效果。
@Override
public boolean onTouchEvent(MotionEvent event) {
boolean handled = mMoveHelper.onTouch(this, event, ((ViewGroup) getParent()).getWidth(), ((ViewGroup) getParent()).getHeight(), getWidth(), getHeight(), mScale, true);
return handled | super.onTouchEvent(event);
}
复制代码
2. StickerMoveHelper 帮助类
在移动类中限制了边界,边界即为父容器的宽高。在左上右下的边界均要判断,判断的逻辑也很简单,即超出父容器后,让视图保持不动,不改变x y 值。
public class StickerMoveHelper {
private static final String TAG = "StickerMoveHelper";
private final View mView;
private float mX, mY;
private static final Matrix M = new Matrix();
public StickerMoveHelper(View view) {
mView = view;
}
//isOverflow: 是否根据padding溢出边界
public boolean onTouch(View v, MotionEvent event, float parentWidth, float parentHeight, float width, float height, float scale, boolean isLimitBorder) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
mX = event.getX();
mY = event.getY();
M.reset();
M.setRotate(v.getRotation());
return true;
case MotionEvent.ACTION_MOVE:
float[] dxy = {event.getX() - mX, event.getY() - mY};
M.mapPoints(dxy);
float x = mView.getTranslationX() + dxy[0];
float y = mView.getTranslationY() + dxy[1];
// 限制边界 (考虑了按钮的宽高, 去掉TEXT_PADDING的计算,则是原先的状态)
if (isLimitBorder) {
if (x < (-StickerConfig.TEXT_PADDING)) x = (-StickerConfig.TEXT_PADDING);
if (x > (parentWidth - width + StickerConfig.TEXT_PADDING))
x = (parentWidth - width + StickerConfig.TEXT_PADDING);
if (y < (-StickerConfig.TEXT_PADDING)) y = -StickerConfig.TEXT_PADDING;
if (y > (parentHeight - height + StickerConfig.TEXT_PADDING))
y = (parentHeight - height + StickerConfig.TEXT_PADDING);
}
v.setTranslationX(x);
v.setTranslationY(y);
return true;
}
return false;
}
}
复制代码
3. StickerView
由于主要的逻辑都在BaseStickerView中,所以StickerView中主要是创建视图,可以创建文字 或者拓展图片。
public class StickerView extends BaseStickerView {
private static final String TAG = "StickerView";
private TextView mTextView;
public StickerView(Context context) {
super(context);
}
@Override
public View onCreateContentView(Context context) {
mTextView = new TextView(context);
mTextView.setPadding(StickerConfig.TEXT_PADDING, StickerConfig.TEXT_PADDING, StickerConfig.TEXT_PADDING, StickerConfig.TEXT_PADDING);
mTextView.setSingleLine();
mTextView.setTextColor(Color.BLACK);
mTextView.setTextSize(50);
return mTextView;
}
public void setText(String text) {
mTextView.setText(text);
}
}
复制代码
4. 使用
主要逻辑都在自定义的类中,使用起来也很方便了。
StickerView stickerView = new StickerView(this);
stickerView.setText("哈哈哈");
container.addView(stickerView);
复制代码