Andorid自定义attr的各种坑

在开发Andorid应用程序中,经常会自定义View来实现各种各样炫酷的效果,在实现这吊炸天效果的同时,我们往往会定义很多attr属性,这样就可以在XML中配置我们想要的属性值,以下就是定义属性值可能遇到的各种坑。

大家都知道怎么定义attr属性,一般如下:

<declare-styleable name="Sample">
   <attr name="custom" format="string|reference" /></declare-styleable>

先声明一个styleable名称,name名称最好见名知义,一个styleable里面可以有多个attr属性,每一个attr都含有一个name,同时需要指明所能赋值的类型,这是是依靠format来定义的。定义好之后就可以在自定义View中使用,来实现各种吊炸天的效果,使用如下:
xml中使用:

<com.sample.ui.widget.Custom   android:id="@+id/custom_view"   android:layout_width="130dp"   android:layout_height="130dp"   android:layout_gravity="center_horizontal"   android:layout_marginTop="90dp"   app:text="@string/custom_desc"
   />

记得声明 xmlns:app="http://schemas.android.com/apk/res-auto", app 可以随便取名
代码中获取值:

TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Sample);
String value = a.getString(R.styleable.Sample.custom);
a.recycle();

根据format不同,还有getDimension,getColor等方式获取值。

上面只是描述了一般定义的方式,但他不是今天的主题,今天的主题是可能遇到的各种坑:

1:项目中只包含一个attr.xml,出现 Attribute "custom" has already been defined,参考链接

<declare-styleable name="Sample">
        <attr name="custom" format="string|reference" /></declare-styleable><declare-styleable name="Sample1">
        <attr name="custom" format="string|reference" /></declare-styleable>

参考链接

如上声明了两个styleable,同时包含了相同的属性custom,这时在编译时会提示Attribute "xxx" has already been defined,表示相同属性重复定义,相同styleable name不能再同一个attr.xml中重复定义,styleable name不一致attir也不能重复定义,attr format属性不影响重复定义结果。因此可以采用如下方法解决该问题:

a:重命名相同属性名,将其中一个改为不同的名字
b:提取重复定义attr,作为公共属性,方式如下:

<attr name="custom" format="string|reference" /><declare-styleable name="Sample">
    <attr name="custom" /></declare-styleable><declare-styleable name="Sample1">
    <attr name="custom" /></declare-styleable>

参考链接

2: 项目中引用了多个外部项目,出现 Attribute "custom" has already been defined
不同的导入项目中,可能包含多个attr.xml,这样在定义时极有可能重复定义,他又分为如下两种情况:

a: 主项目,引用库包含同名styleable name,如:
主项目:

<declare-styleable name="Sample">
    <attr name="custom" /></declare-styleable>

引用库:

<declare-styleable name="Sample">
    <attr name="custom" /></declare-styleable>

这种情况下,编译是不会出现错误的,可以正常编译。

b: 主项目,引用库包含不同名styleable,但是有同名attr,如;
主项目:

<declare-styleable name="Sample">
    <attr name="custom" /></declare-styleable>

引用库:

<declare-styleable name="Sample1">
    <attr name="custom" /></declare-styleable>

编译时会出现 Attribute "custom" has already been defined。由此可以得出,在项目中引用各种库,模块时,各个不同的模块定义attr,要遵循以下规则,
1:全部不能重复定义,全部不能重复很难实现,不同的团队,不同的产品是极有可能重复定义,因此该方式很难实现。
2:各个不同模块,定义时加上模块前缀,这种方式重复几率就小很多,编译时再将重复的重命名就ok了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是一个简单的自定义View实现K线图的代码示例: ``` public class KLineView extends View { private List<KLineData> mData; // K线数据 private int mWidth, mHeight; // 控件宽度、高度 private int mTopPadding; // 顶部留白 private int mBottomPadding; // 底部留白 private Paint mLinePaint; // K线画笔 private Paint mTextPaint; // 文本画笔 private float mMaxPrice, mMinPrice; // 最高价、最低价 private float mPriceScale; // 价格缩放比例 private float mVolumeScale; // 成交量缩放比例 private int mCandleWidth; // K线宽度 private int mCandleSpace; // K线间隔 public KLineView(Context context) { this(context, null); } public KLineView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public KLineView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); // 初始化画笔 mLinePaint = new Paint(); mLinePaint.setStrokeWidth(2f); mLinePaint.setAntiAlias(true); mLinePaint.setStyle(Paint.Style.STROKE); mTextPaint = new Paint(); mTextPaint.setTextSize(24f); mTextPaint.setAntiAlias(true); mTextPaint.setColor(Color.BLACK); // 初始化K线宽度、间隔 mCandleWidth = dp2px(8); mCandleSpace = dp2px(4); // 初始化留白 mTopPadding = dp2px(20); mBottomPadding = dp2px(20); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); // 获取控件宽度、高度 mWidth = MeasureSpec.getSize(widthMeasureSpec); mHeight = MeasureSpec.getSize(heightMeasureSpec); // 计算价格、成交量缩放比例 mPriceScale = (mHeight - mTopPadding - mBottomPadding) / (mMaxPrice - mMinPrice); mVolumeScale = (mHeight - mTopPadding - mBottomPadding) / getMaxVolume(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 绘制K线图 drawCandles(canvas); // 绘制边框 drawBorder(canvas); // 绘制Y轴标签 drawYLabels(canvas); } /** * 绘制K线图 */ private void drawCandles(Canvas canvas) { if (mData == null || mData.size() == 0) { return; } mLinePaint.setColor(Color.RED); mLinePaint.setStyle(Paint.Style.FILL); float startX = mCandleWidth / 2 + mCandleSpace; // 第一个K线起始X坐标 float startY = 0f; // K线起始Y坐标 float endX, endY; // K线结束X坐标、Y坐标 for (int i = 0; i < mData.size(); i++) { KLineData data = mData.get(i); // 计算K线顶部、底部Y坐标 float highY = mTopPadding + (mMaxPrice - data.getHigh()) * mPriceScale; float lowY = mTopPadding + (mMaxPrice - data.getLow()) * mPriceScale; // 计算K线开盘、收盘Y坐标 float openY = mTopPadding + (mMaxPrice - data.getOpen()) * mPriceScale; float closeY = mTopPadding + (mMaxPrice - data.getClose()) * mPriceScale; // 绘制K线实体 if (data.getOpen() < data.getClose()) { mLinePaint.setColor(Color.RED); } else { mLinePaint.setColor(Color.GREEN); } canvas.drawRect(startX - mCandleWidth / 2, closeY, startX + mCandleWidth / 2, openY, mLinePaint); // 绘制K线上下影线 mLinePaint.setColor(Color.BLACK); canvas.drawLine(startX, highY, startX, openY, mLinePaint); canvas.drawLine(startX, lowY, startX, closeY, mLinePaint); // 更新起始X坐标 startX += mCandleWidth + mCandleSpace; } } /** * 绘制边框 */ private void drawBorder(Canvas canvas) { mLinePaint.setColor(Color.BLACK); mLinePaint.setStyle(Paint.Style.STROKE); canvas.drawRect(0, 0, mWidth, mHeight, mLinePaint); } /** * 绘制Y轴标签 */ private void drawYLabels(Canvas canvas) { float labelHeight = mTextPaint.descent() - mTextPaint.ascent(); // 绘制价格标签 float priceLabelY = mTopPadding - mTextPaint.ascent(); for (float price = mMinPrice; price <= mMaxPrice; price += (mMaxPrice - mMinPrice) / 5) { float priceY = mTopPadding + (mMaxPrice - price) * mPriceScale; canvas.drawText(String.format(Locale.getDefault(), "%.2f", price), 0, priceY + labelHeight / 2, mTextPaint); canvas.drawLine(mCandleWidth / 2, priceY, mWidth, priceY, mLinePaint); } // 绘制成交量标签 float volumeLabelY = mHeight - mBottomPadding - mTextPaint.descent(); float maxVolume = getMaxVolume(); for (int i = 0; i < 5; i++) { float volumeY = volumeLabelY - i * labelHeight * maxVolume / 5; canvas.drawText(String.format(Locale.getDefault(), "%.0f", maxVolume * i / 5), 0, volumeY - labelHeight / 2, mTextPaint); } } /** * 设置K线数据 */ public void setData(List<KLineData> data) { mData = data; calculateMaxAndMinPrice(); requestLayout(); invalidate(); } /** * 计算最高价、最低价 */ private void calculateMaxAndMinPrice() { if (mData == null || mData.size() == 0) { return; } mMaxPrice = mData.get(0).getHigh(); mMinPrice = mData.get(0).getLow(); for (int i = 1; i < mData.size(); i++) { KLineData data = mData.get(i); mMaxPrice = Math.max(mMaxPrice, data.getHigh()); mMinPrice = Math.min(mMinPrice, data.getLow()); } } /** * 获取最大成交量 */ private float getMaxVolume() { float maxVolume = 0f; if (mData != null && mData.size() > 0) { for (KLineData data : mData) { maxVolume = Math.max(maxVolume, data.getVolume()); } } return maxVolume; } /** * dp转px */ private int dp2px(int dp) { return (int) (dp * getContext().getResources().getDisplayMetrics().density + 0.5f); } } ``` 其中,KLineData表示K线数据,包括开盘价、收盘价、最高价、最低价、成交量等属性。可以根据实际需求进行修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lmr廖

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值