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

一个自定义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 rule            if (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 layout            FrameLayout 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 layout            labelViewContainer.addView(target);            labelViewContainer.addView(this);            labelViewContainer.setId(_labelViewContainerID);            // add dummy layout in parent container            parentContainer.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 {    @Override    protected 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() {                        @Override                        public 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() {                        @Override                        public 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() {                        @Override                        public 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() {                        @Override                        public 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() {                        @Override                        public void onClick(View v) {                            Intent i = new Intent(MainActivity.this,                                    ListViewActivity.class);                            startActivity(i);                        }                    });        }    }    @Override    public 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);        }        @Override        public int getItemResource() {            return R.layout.list_view_item;        }        @Override        public 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 manual            LabelView 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;    @Override    protected 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

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

更多相关文章

  1. Animations使用(一)
  2. android ScrollView 与SeekBar结合使用
  3. Android使用ValueAnimator开发仿余额宝数值增加控件
  4. Android(安卓)M新控件之AppBarLayout,NavigationView,CoordinatorL
  5. Android(安卓)多颜色主题框架MagicaSakura使用
  6. Android(安卓)三角形控件
  7. android 获取控件大小
  8. 如何申请google map api key
  9. Android(安卓)ImageView点击变暗效果

随机推荐

  1. Android(安卓)Studio、Android(安卓)SDK
  2. LinearLayout布局之weight
  3. android 拍照+从手机相册选择返回图片到i
  4. Android(安卓)生成keystore(user & debug
  5. android:visibility="gone"
  6. android studio中运行main方法报错问题解
  7. Android(安卓)Studio 解决方法No JVM ins
  8. android:xml创建menu
  9. Android(安卓)判断SD卡是否存在及容量查
  10. Android(安卓)TabHost使用、动态加载内容