如果有玩过nice这款APP的话,里面有个在图片上打标签的功能,如图
实现原理:
1:实现自定义一个ViewGroup类型,设置好它的背景,
2:在onTouch down的时候,获取相对于view的按下的x,y坐标,然后遍历所有的子view判断是否重叠因为每个view其实都是一个矩形,Rect类方法中可以判断该点是否在view上,
大概就这些,下面是实现的源码:
PictureTagLayout.java
package com.example.tagview.view;
import com.example.tagview.view.TagView.Direction;
import com.example.tagview.view.TagView.Status;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.RelativeLayout;
public class PictureTagLayout extends RelativeLayout implements OnTouchListener {
private static final int CLICKRANGE = 5;
private static final String TAG = PictureTagLayout.class.getSimpleName();
private View touchView,clickView;//单机和触摸都可以打标签
private int startX;//按下的x轴坐标
private int startY;//按下的y轴坐标
@SuppressLint("ClickableViewAccessibility")
public PictureTagLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setOnTouchListener(this);
}
public PictureTagLayout(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public PictureTagLayout(Context context) {
this(context,null);
}
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touchView = null;
if(clickView!=null){//这个clickView什么时候给它赋值呢?
((TagView)clickView).setStatus(Status.Normal);
clickView = null;
}
startX = (int) event.getX();//获取按下的x轴坐标 相对于view本身的左上角
startY = (int) event.getY();//获取按下的y轴坐标,相对于view本身
if(!hasView(startX,startY)){//表示重叠
addItem(startX,startY);
}
break;
case MotionEvent.ACTION_UP:
int endX = (int) event.getX();
int endY = (int) event.getY();
//如果挪动的范围很小,则判定为单击
if(touchView!=null&&Math.abs(endX - startX)<CLICKRANGE&&Math.abs(endY - startY)<CLICKRANGE){
//当前点击的view进入编辑状态
((TagView)touchView).setStatus(Status.Edit);
clickView = touchView;
}
touchView = null;
break;
}
return true;
}
//循环获取子view,判断xy是否在子view上,即判断是否按住了子view
private boolean hasView(int x,int y){
for(int index = 0; index < this.getChildCount(); index ++){//遍历相对布局中所有的子view然后生成一个矩形,
View view = this.getChildAt(index);
int left = (int) view.getX();
int top = (int) view.getY();
int right = view.getRight();
int bottom = view.getBottom();
Rect rect = new Rect(left, top, right, bottom);//生成一个矩形
Rect newRect = new Rect(x,y,x+TagView.getViewWidth(),y+TagView.getViewHeight());
boolean isContains = rect.intersect(newRect);//判断view是否交互
//如果没有交互
if(isContains){
touchView = view;
touchView.bringToFront();//置为父view之上
return true;
}
}
touchView = null;
return false;
}
private void addItem(int x,int y){
View view = null;//就要新生成一个新的view
RelativeLayout.LayoutParams params=new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);
if(x>getWidth()*0.5){//如果x坐标大于view宽度的一半 用来判断是在左边还是右边
// params.leftMargin = x - TagView.getViewWidth();
params.leftMargin = x;
view = new TagView(getContext(),Direction.Right);//生成一个view对象
}
else{
params.leftMargin = x;
view = new TagView(getContext(),Direction.Left);
}
params.topMargin = y;//向上就是他的y坐标
//上下位置在视图内
if(params.topMargin<0)params.topMargin =0;//边界判断
else if((params.topMargin+TagView.getViewHeight())>getHeight()){//判断这个view 在 这个图片上 防止部分
params.topMargin = getHeight() - TagView.getViewHeight();
}
this.addView(view, params);
}
}
TagView.java 标签view
package com.example.tagview.view;
import com.example.tagview.R;
import android.content.Context;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
/**
* tag view
* @author Adminis
*
*/
public class TagView extends RelativeLayout implements OnEditorActionListener {
private TextView tvPictureTagLabel;
private EditText etPictureTagLabel;
private View loTag;
private InputMethodManager mInputMethodManager;
public enum Status{Normal,Edit}//标签是编辑状态还是正常的状态
public enum Direction{Left,Right}
private Direction direction = Direction.Left;//标签是左边还是右边
private Context mContext;
private static final int ViewWidth = 80;//单位像素
private static final int ViewHeight = 50;
public TagView(Context context, Direction direction) {
super(context);
this.direction = direction;
this.mContext = context;
initViews();
init();
initEvents();
}
private void init() {
mInputMethodManager = (InputMethodManager)mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
directionChange();
}
private void directionChange() {
switch(direction){
case Left:
loTag.setBackgroundResource(R.drawable.bg_picturetagview_tagview_left);
break;
case Right:
loTag.setBackgroundResource(R.drawable.bg_picturetagview_tagview_right);
break;
}
}
/**
* 初始化view
*/
private void initViews() {
LayoutInflater.from(mContext).inflate(R.layout.activity_tag_view, this,true);
tvPictureTagLabel = (TextView) findViewById(R.id.tvPictureTagLabel);
etPictureTagLabel = (EditText) findViewById(R.id.etPictureTagLabel);
loTag = findViewById(R.id.loTag);
}
/** 初始化事件 **/
protected void initEvents(){
etPictureTagLabel.setOnEditorActionListener(this);
}
public static int getViewWidth(){
return ViewWidth;
}
public static int getViewHeight(){
return ViewHeight;
}
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
setStatus(Status.Normal);
return true;
}
public void setStatus(Status status){
switch(status){
case Normal:
tvPictureTagLabel.setVisibility(View.VISIBLE);
etPictureTagLabel.clearFocus();
tvPictureTagLabel.setText(etPictureTagLabel.getText());
etPictureTagLabel.setVisibility(View.GONE);
//隐藏键盘
mInputMethodManager.hideSoftInputFromWindow(etPictureTagLabel.getWindowToken() , 0);
break;
case Edit:
tvPictureTagLabel.setVisibility(View.GONE);
etPictureTagLabel.setVisibility(View.VISIBLE);
etPictureTagLabel.requestFocus();
//弹出键盘
mInputMethodManager.toggleSoftInput(0, InputMethodManager.SHOW_FORCED);
break;
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
View parent = (View) getParent();//父view就是PictureTagLayout
int halfParentW = (int) (parent.getWidth()*0.5);//获取父view宽度的一半
int center = (int) (l + (this.getWidth()*0.5));
if(center<=halfParentW){
direction = Direction.Left;
}
else{
direction = Direction.Right;
}
directionChange();
}
}
activity_tag_view.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal" >
<RelativeLayout
android:id="@+id/loTag"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/bg_picturetagview_tagview_left" >
<TextView
android:id="@+id/tvPictureTagLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@null"
android:gravity="center_vertical"
android:hint="标签"
android:singleLine="true"
android:textColor="@android:color/white"
android:textColorHint="@android:color/white"
android:textSize="12dp" />
<EditText
android:id="@+id/etPictureTagLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@null"
android:gravity="center_vertical"
android:hint="标签"
android:imeOptions="actionDone"
android:maxEms="20"
android:singleLine="true"
android:textColor="@android:color/white"
android:textColorHint="@android:color/white"
android:textSize="12dp"
android:visibility="gone" />
</RelativeLayout>
</LinearLayout>
最终的效果: