转载请注明出处:王亟亟的大牛之路

一个自定义TextView可实现各种控件右上,左上等位置附带便签实现。

项目结构:

运行效果:

只需要一个类就可以完成以上实现
LabelView

public class LabelView extends TextView {private float _offsetx;private float _offsety;private float _anchorx;private float _anchory;private float _angel;private int _labelViewContainerID;private Animation _animation = new Animation() {protected void applyTransformation(float interpolatedTime, Transformation t) {Matrix tran = t.getMatrix();tran.postTranslate(_offsetx, _offsety);tran.postRotate(_angel, _anchorx, _anchory);}};public enum Gravity {LEFT_TOP, RIGHT_TOP}public LabelView(Context context) {this(context, null);}public LabelView(Context context, AttributeSet attrs) {this(context, attrs, android.R.attr.textViewStyle);}@SuppressLint("NewApi")public LabelView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);init();_animation.setFillBefore(true);_animation.setFillAfter(true);_animation.setFillEnabled(true);}private void init() {if (!(getLayoutParams() instanceof ViewGroup.LayoutParams)) {LayoutParams layoutParams =new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT);setLayoutParams(layoutParams);}// the default value//setPadding(dip2Px(40), dip2Px(2), dip2Px(40), dip2Px(2));_labelViewContainerID = -1;setGravity(android.view.Gravity.CENTER);setTextColor(Color.WHITE);setTypeface(Typeface.DEFAULT_BOLD);setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);setBackgroundColor(Color.BLUE);}public void setTargetView(View target, int distance, Gravity gravity) {if (!replaceLayout(target)) {return;}final int d = dip2Px(distance);final Gravity g = gravity;final View v = target;ViewTreeObserver vto = getViewTreeObserver();vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {public void onGlobalLayout() {getViewTreeObserver().removeGlobalOnLayoutListener(this);calcOffset(getMeasuredWidth(), d, g, v.getMeasuredWidth(), false);}});}public void setTargetViewInBaseAdapter(View target, int targetWidth, int distance, Gravity gravity) {if (!replaceLayout(target)) {return;}//measure(0, 0);//calcOffset(getMeasuredWidth(), distance, gravity, targetWidth, true);calcOffset(dip2Px(targetWidth), distance, gravity, targetWidth, true);}public void remove() {if (getParent() == null || _labelViewContainerID == -1) {return;}ViewGroup frameContainer = (ViewGroup) getParent();assert (frameContainer.getChildCount() == 2);View target = frameContainer.getChildAt(0);ViewGroup parentContainer = (ViewGroup) frameContainer.getParent();int groupIndex = parentContainer.indexOfChild(frameContainer);if (frameContainer.getParent() instanceof RelativeLayout) {for (int i = 0; i < parentContainer.getChildCount(); i++) {if (i == groupIndex) {continue;}View view = parentContainer.getChildAt(i);RelativeLayout.LayoutParams para = (RelativeLayout.LayoutParams) view.getLayoutParams();for (int j = 0; j < para.getRules().length; j++) {if (para.getRules()[j] == _labelViewContainerID) {para.getRules()[j] = target.getId();}}view.setLayoutParams(para);}}ViewGroup.LayoutParams frameLayoutParam = frameContainer.getLayoutParams();target.setLayoutParams(frameLayoutParam);parentContainer.removeViewAt(groupIndex);frameContainer.removeView(target);frameContainer.removeView(this);parentContainer.addView(target,groupIndex);_labelViewContainerID = -1;}@SuppressLint("NewApi")private boolean replaceLayout(View target) {if (getParent() != null || target == null || target.getParent() == null || _labelViewContainerID != -1) {return false;}ViewGroup parentContainer = (ViewGroup) target.getParent();if (target.getParent() instanceof FrameLayout) {((FrameLayout) target.getParent()).addView(this);} else if (target.getParent() instanceof ViewGroup) {int groupIndex = parentContainer.indexOfChild(target);_labelViewContainerID = generateViewId();// relativeLayout need copy ruleif (target.getParent() instanceof RelativeLayout) {for (int i = 0; i < parentContainer.getChildCount(); i++) {if (i == groupIndex) {continue;}View view = parentContainer.getChildAt(i);RelativeLayout.LayoutParams para = (RelativeLayout.LayoutParams) view.getLayoutParams();for (int j = 0; j < para.getRules().length; j++) {if (para.getRules()[j] == target.getId()) {para.getRules()[j] = _labelViewContainerID;}}view.setLayoutParams(para);}}parentContainer.removeView(target);// new dummy layoutFrameLayout labelViewContainer = new FrameLayout(getContext());ViewGroup.LayoutParams targetLayoutParam = target.getLayoutParams();labelViewContainer.setLayoutParams(targetLayoutParam);target.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));// add target and label in dummy layoutlabelViewContainer.addView(target);labelViewContainer.addView(this);labelViewContainer.setId(_labelViewContainerID);// add dummy layout in parent containerparentContainer.addView(labelViewContainer, groupIndex, targetLayoutParam);}return true;}private void calcOffset(int labelWidth, int distance, Gravity gravity, int targetWidth, boolean isDP) {int d = dip2Px(distance);int tw = isDP ? dip2Px(targetWidth) : targetWidth;float edge = (float) ((labelWidth - 2 * d) / (2 * 1.414));if (gravity == Gravity.LEFT_TOP) {_anchorx = -edge;_offsetx = _anchorx;_angel = -45;} else if (gravity == Gravity.RIGHT_TOP) {_offsetx = tw + edge - labelWidth;_anchorx = tw + edge;_angel = 45;}_anchory = (float) (1.414 * d + edge);_offsety = _anchory;clearAnimation();startAnimation(_animation);}private int dip2Px(float dip) {return (int) (dip * getContext().getResources().getDisplayMetrics().density + 0.5f);}private static final AtomicInteger sNextGeneratedId = new AtomicInteger(1);public static int generateViewId() {for (; ; ) {final int result = sNextGeneratedId.get();// aapt-generated IDs have the high byte nonzero; clamp to the range under that.int newValue = result + 1;if (newValue > 0x00FFFFFF) newValue = 1; // Roll over to 1, not 0.if (sNextGeneratedId.compareAndSet(result, newValue)) {return result;}}}
}

列出一些比较重要的内容,便于使用,修改:

整体布局的位置,左上以及右上,如果需要其他位置可自行添加

 public enum Gravity {LEFT_TOP, RIGHT_TOP}

初始化参数:
字体大小:setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
背景颜色:setBackgroundColor(Color.BLUE);
字体颜色:setTextColor(Color.WHITE);
位置:setGravity(android.view.Gravity.CENTER);
字体风格:setTypeface(Typeface.DEFAULT_BOLD);

一般控件设置用

public void setTargetView(View target, int distance, Gravity gravity)

适配器控件设置用

  public void setTargetViewInBaseAdapter(View target, int targetWidth, int distance, Gravity gravity)

让标签消失

 public void remove()

MainActivity

public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);{final LabelView label = new LabelView(this);//涂鸦部分文字的内容label.setText("大长腿");//涂鸦部分的颜色label.setBackgroundColor(0x0EfE32E20);// public void setTargetView(View target, int distance, Gravity gravity) //3个参数,一个是控件的ID,填充带的长度或者说便宜的距离(反正越大那一条东西越长),填充带的位置label.setTargetView(findViewById(R.id.image1), 30,LabelView.Gravity.LEFT_TOP);//监听事件findViewById(R.id.image1).setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {//填充带消失label.remove();//吐司内容Toast.makeText(MainActivity.this,"大长腿消失了", Toast.LENGTH_SHORT).show();}});}{//效果同上,只是涂鸦层位置的变化final LabelView label = new LabelView(this);label.setText("绝对领域");label.setBackgroundColor(0xff491E23);label.setTargetView(findViewById(R.id.image2), 22,LabelView.Gravity.RIGHT_TOP);findViewById(R.id.image2).setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {label.remove();Toast.makeText(MainActivity.this,"绝对领域消失了", Toast.LENGTH_SHORT).show();}});}{//Button也适用LabelView label = new LabelView(this);label.setText("按钮");label.setBackgroundColor(0xffE91E63);//位于右上角label.setTargetView(findViewById(R.id.button), 14,LabelView.Gravity.RIGHT_TOP);findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Toast.makeText(MainActivity.this, "button click",Toast.LENGTH_SHORT).show();}});}{//TextView适用LabelView label = new LabelView(this);label.setText("Text");label.setBackgroundColor(0xff03a9f4);label.setTargetView(findViewById(R.id.text), 11,LabelView.Gravity.LEFT_TOP);findViewById(R.id.text).setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Toast.makeText(MainActivity.this,"please click ListView Demo",Toast.LENGTH_SHORT).show();}});}{LabelView label = new LabelView(this);label.setText("List");label.setBackgroundColor(0xff03a9f4);label.setTargetView(findViewById(R.id.click), 8,LabelView.Gravity.RIGHT_TOP);findViewById(R.id.click).setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Intent i = new Intent(MainActivity.this,ListViewActivity.class);startActivity(i);}});}}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {getMenuInflater().inflate(R.menu.menu_main, menu);return true;}}

布局文件:

<LinearLayout 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:orientation="vertical"android:paddingBottom="@dimen/activity_vertical_margin"android:paddingLeft="@dimen/activity_horizontal_margin"android:paddingRight="@dimen/activity_horizontal_margin"android:paddingTop="@dimen/activity_vertical_margin"tools:context=".MainActivity"><Button
        android:id="@+id/button"android:layout_width="200dp"android:layout_height="48dp"android:background="#03a9f4"android:text="按钮"android:textColor="#ffffff" /><LinearLayout
        android:layout_marginTop="24dp"android:layout_width="match_parent"android:layout_height="wrap_content"><ImageView
            android:id="@+id/image1"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"android:scaleType="centerCrop"android:src="@mipmap/image1" /><ImageView
            android:id="@+id/image2"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"android:scaleType="centerCrop"android:src="@mipmap/image2" /></LinearLayout><TextView
        android:id="@+id/text"android:layout_marginTop="24dp"android:layout_width="wrap_content"android:padding="16dp"android:background="#212121"android:layout_gravity="center"android:gravity="center"android:text="TextView"android:textColor="#ffffff"android:layout_height="48dp" /><Button
        android:id="@+id/click"android:layout_marginTop="20dp"android:layout_width="200dp"android:layout_gravity="center_horizontal"android:layout_height="48dp"android:background="#E91E63"android:text="点击进入ListView"android:textColor="#ffffff" /></LinearLayout>

ListViewActivity

public class ListViewActivity extends Activity {public class CategoryData {public String image;public String text;public String label;}public class CategoryAdapter extends SimpleBaseAdapter<CategoryData> {public CategoryAdapter(Context context, List<CategoryData> data) {super(context, data);}@Overridepublic int getItemResource() {return R.layout.list_view_item;}@Overridepublic View getItemView(int position, View convertView, ViewHolder holder) {CategoryData item = (CategoryData) _categoryAdapter.getItem(position);TextView textView = holder.getView(R.id.text);textView.setText(item.text);ImageView imageView = holder.getView(R.id.image);imageView.setImageResource(Integer.parseInt(item.image));// you have to generate label ID manualLabelView label = holder.getView(12345);if (label == null) {label = new LabelView(ListViewActivity.this);label.setId(12345);label.setBackgroundColor(0xffE91E63);// public void setTargetViewInBaseAdapter(View target, int targetWidth, int distance, Gravity gravity)//传入4个参数,被画的控件,label中text的位置(数字越小越靠右),整个标签便宜的位置,总体所在的区域label.setTargetViewInBaseAdapter(imageView, 108, 25, LabelView.Gravity.LEFT_TOP);}label.setText(item.label);return convertView;}}private CategoryAdapter _categoryAdapter;private ListView _listView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_list_view);_listView = (ListView) findViewById(R.id.list_view);_categoryAdapter = new CategoryAdapter(this, null);_listView.setAdapter(_categoryAdapter);//重复三轮妹子getCategoryData();getCategoryData();getCategoryData();}private void getCategoryData() {List<CategoryData> data = new ArrayList<CategoryData>();{CategoryData item = new CategoryData();item.text = "妹子好看";item.image = R.mipmap.k1 + "";item.label = "妹子1";data.add(item);}{CategoryData item = new CategoryData();item.text = "绝对领域";item.image = R.mipmap.k2 + "";item.label = "妹子2";data.add(item);}{CategoryData item = new CategoryData();item.text = "妹子好看1";item.image = R.mipmap.k3 + "";item.label = "妹子3";data.add(item);}{CategoryData item = new CategoryData();item.text = "绝对领域1";item.image = R.mipmap.k4 + "";item.label = "妹子4";data.add(item);}{CategoryData item = new CategoryData();item.text = "啪啪啪";item.image = R.mipmap.k5 + "";item.label = "妹子5";data.add(item);}{CategoryData item = new CategoryData();item.text = "萌萌哒,呵呵哒";item.image = R.mipmap.k6 + "";item.label = "妹子6";data.add(item);}{CategoryData item = new CategoryData();item.text = "肉便器噼里啪啦";item.image = R.mipmap.k7 + "";item.label = "妹子7";data.add(item);}{CategoryData item = new CategoryData();item.text = "稀里哗啦";item.image = R.mipmap.k8 + "";item.label = "妹子8";data.add(item);}{CategoryData item = new CategoryData();item.text = "咖喱给给";item.image = R.mipmap.k9 + "";item.label = "妹子9";data.add(item);}{CategoryData item = new CategoryData();item.text = "呵呵哈hi";item.image = R.mipmap.k10 + "";item.label = "妹子10";data.add(item);}_categoryAdapter.addAll(data);_categoryAdapter.notifyDataSetChanged();}}

其他一些只是为了实现而写了,可以直接看源码
源码地址:http://yunpan.cn/cmxxzBp8T8MSX 访问密码 7dcc

部分内容参考网上,如有雷同,经供参考

android 自定义LabelView实现各类小标签,重要功能已标注相关推荐

  1. android手机微信收藏功能实现,Android模仿微信收藏文件的标签处理功能

    最近需要用到微信的标签功能(如下图所示).该功能可以添加已有标签,也可以自定义标签.也可以删除已编辑菜单.研究了一番.发现还是挺有意思的,模拟实现相关功能. 该功能使用类似FlowLayout的功能. ...

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

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

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

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

  4. Android 自定义动画view(小变大,旋转,色值)

    也不知道到看了多少的动画总结了,但是用到的时候太少,过段时间就会忘记了. 既然如此,我选择直接去动手学习,步步进阶. 效果: 上代码之前我们分析一下才会加深自己的印象: 需要画一个矩形 和 一个圆形 ...

  5. Android自定义实现按周签到打卡功能

    之前实现过<Android可签到的日历控件>的功能,跟这篇一样都是实现签到打卡功能,这篇实现的是按月进行打卡做标识,本篇内容实现的按周进行签到打卡. 实现签到规则如下: 1.连续签到7天, ...

  6. 安android自定义照相机,Android自定义照相机的实例

    Android自定义照相机实现 近期小巫在学校有一个创新项目,也不是最近,是一个拖了很久的项目,之前一直没有去搞,最近因为要中期检查,搞得我跟小组成员一阵忙活,其实开发一款照相机软件并不太难,下面就是 ...

  7. android 自定义相机,Android自定义相机实现定时拍照功能

    这篇博客为大家介绍Android自定义相机,并且实现倒计时拍照功能. 首先自定义拍照会用到SurfaceView控件显示照片的预览区域,以下是布局文件: activity_main.xml andro ...

  8. android 画布叠加,Android自定义图形,图形的拼接、叠加、相容

    直接上Xfermode子类: AvoidXfermode  指定了一个颜色和容差,强制Paint避免在它上面绘图(或者只在它上面绘图). PixelXorXfermode  当覆盖已有的颜色时,应用一 ...

  9. android 实现自动拍照,Android自定义相机实现定时拍照功能

    这篇博客为大家介绍Android自定义相机,并且实现倒计时拍照功能. 首先自定义拍照会用到SurfaceView控件显示照片的预览区域,以下是布局文件: activity_main.xml andro ...

最新文章

  1. 运算符中的二元重载,为什么要调用友元函数而不是全局函数的问题
  2. 算法------二叉树的层序遍历
  3. python yield遍历目录
  4. FreePascal - 如何在各个平台中安装CodeTyphon!
  5. Android --- 屏幕方向screenOrientation属性详解
  6. 如何把配置文件集成到.exe中_VSCode如何使用cmder
  7. Golang之envconfig的使用
  8. VB程序逆向常用的函数
  9. 经典排序算法 - 鸡尾酒排序Cocktail sort
  10. 2021年高考成绩查询山东德洲,2021年德州高考状元是谁分数多少分,历年德州高考状元名单...
  11. 安卓休眠唤醒ajax休线程,休眠无法获得远程服务器上当前线程的事务同步会话...
  12. mysql insert 主键冲突_在MySql中建立存储过程和解决insert into select 中主键冲突的有关问题...
  13. 基于WebActivator的改进版本KudyStudio.Web.Activating讲解与下载
  14. Jquery关闭离开页面时提醒
  15. java实现KMZ文件转KML文件
  16. SOLD格雷母线是什么?
  17. 全网最全最细的vmware虚拟机创建教程,一步一步教你完成虚拟机创建。
  18. 【Python入门练手小项目】画一个蓝胖子❤️
  19. python 对比matlab_全面对比 MATLAB、Julia、Python,谁在科学计算中更胜一筹?
  20. 小程序连接优声云打印机流程

热门文章

  1. Surciata源码分析之IpsNFQ模式(1)
  2. Python爬虫实现无api版本各国汇率实时兑换(美元、英镑等)
  3. 栈——栈的基本概念和基本操作
  4. Automated Phrase
  5. c语言程序考试试题,C语言程序设计期末考试试题(含答案)
  6. vue之push() pop() shift() unshift() splice() sort() reverse()等等
  7. swust2020春季《形势与政策》mooc考试答案参考
  8. nanohttp简单使用
  9. 视频监控网络传输计算方法
  10. apidoc写php接口文档