关于自定义控件以及自定义属性的文章,可以查看 Android高手进阶教程(四)之----Android 中自定义属性(attr.xml,TypedArray)的使用!。本文主要在其基础之上结合实际开发当中遇到的问题,举例分析一下在使用TypedArray.getDimension时应当注意的问题。
一、主要代码以及不同手机显示图片
主要显示代码main.xml关键代码:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:test="http://schemas.android.com/apk/res/com.yang"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:background="@color/background">
<TextView android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:textColor="@android:color/black"
android:textSize="18sp"
android:text="@string/shiyan"/>
<com.yang.widget.TextViewAndSpaner
android:layout_width="fill_parent"
android:layout_height="wrap_content"
test:item_content_text="@string/shiyan"
test:item_content_textsize="18sp"
test:item_content_textcolor="@android:color/black"
test:item_option_prompt="@string/please_select"
test:item_option_values="@array/tureorfalse" />
</LinearLayout>
text_spaner_view.xml代码:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1.33"
android:orientation="vertical" >
<TextView
android:id="@+id/item_content"
android:layout_width="240dip"
android:layout_height="wrap_content"
android:singleLine="false"
android:textSize="15sp" />
<TextView
android:id="@+id/item_remark_str"
android:layout_width="240dip"
android:layout_height="wrap_content"
android:singleLine="false"
android:textSize="10sp"
android:visibility="gone"/>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="vertical" >
<Button
android:id="@+id/item_option"
android:layout_width="80dip"
android:layout_height="45dip"
android:text="@string/please_select"
android:textSize="13sp"
/>
<TextView
android:id="@+id/item_option_ps"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="false"
android:textSize="10sp"
android:layout_gravity="center"
android:visibility="gone" />
<Button
android:id="@+id/item_remark"
android:layout_width="80dip"
android:layout_height="30dip"
android:text="备注"
android:textSize="10sp"
android:visibility="gone"
/>
</LinearLayout>
</LinearLayout>
<View
android:layout_width="fill_parent"
android:layout_height="1dp"
android:background="@android:color/white" />
</LinearLayout>
TextViewAndSpaner.java
package com.yang.widget;
import com.yang.R;
import com.yang.utils.Logger;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.TypedArray;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
public class TextViewAndSpaner extends LinearLayout {
private TextView item_content;
private Button item_remark;
// 1、使用startActivityforResult或者
// 2、设置Intent的Type属性
// 但是使用后者时需要将原先的Activity传过来,在TextViewAndSpaner中添加代码。
// 同时,要开启Activity当中设置判断条件。开发复杂。因此使用前者。
private TextView item_remark_str;
private Button item_option;
public Button getItem_option() {
return item_option;
}
public void setItem_option(Button item_option) {
this.item_option = item_option;
}
private TextView item_option_ps;
private Context mContext;
private boolean isopposites;
/**
* 默认构造函数
*/
public TextViewAndSpaner(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
mContext = context;
LayoutInflater.from(context).inflate(R.layout.text_spaner_view, this,
true);
item_content = (TextView) findViewById(R.id.item_content);
item_remark = (Button) findViewById(R.id.item_remark);
item_option = (Button) findViewById(R.id.item_option);
item_remark_str = (TextView) findViewById(R.id.item_remark_str);
item_option_ps = (TextView) findViewById(R.id.item_option_ps);
TypedArray typeArray = context.obtainStyledAttributes(attrs,
R.styleable.TextViewAndSpaner);
setItemContext(typeArray);
setItemRemark(typeArray);
// 设置相关的点击选项
setOption(typeArray);
setRemark(typeArray);
// 回收
typeArray.recycle();
}
private void setRemark(TypedArray typeArray) {
// TODO Auto-generated method stub
boolean remarkFlag = typeArray.getBoolean(
R.styleable.TextViewAndSpaner_is_remark_enable, false);
if (remarkFlag) {
item_remark.setVisibility(View.VISIBLE);
item_remark.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// TODO Auto-generated method stub
remarkDialog();
}
});
}
}
private void setItemContext(TypedArray typeArray) {
int contentColor = typeArray.getColor(
R.styleable.TextViewAndSpaner_item_content_textcolor,
0XFFFFFFFF);
item_content.setTextColor(contentColor);
Logger.d("getDimensionPixelOffset"
+ typeArray
.getDimensionPixelOffset(
R.styleable.TextViewAndSpaner_item_content_textsize,
15));
Logger.d("getDimensionPixelSize"
+ typeArray
.getDimensionPixelSize(
R.styleable.TextViewAndSpaner_item_content_textsize,
15));
Logger.d("getLayoutDimension"
+ typeArray
.getLayoutDimension(
R.styleable.TextViewAndSpaner_item_content_textsize,
15));
float contentSize = typeArray.getDimension(
R.styleable.TextViewAndSpaner_item_content_textsize, 15);
Logger.d("contentSize is " + contentSize);
item_content.setTextSize(TypedValue.COMPLEX_UNIT_SP, contentSize);
String contentStr = typeArray
.getString(R.styleable.TextViewAndSpaner_item_content_text);
item_content.setText(contentStr);
}
private void setItemRemark(TypedArray typeArray) {
boolean remarkFlag = typeArray.getBoolean(
R.styleable.TextViewAndSpaner_is_remark_enable, false);
if (remarkFlag) {
int remarkColor = typeArray.getColor(
R.styleable.TextViewAndSpaner_item_remark_textcolor,
0XFFFFFFFF);
item_remark_str.setTextColor(remarkColor);
float remarkSize = typeArray.getDimension(
R.styleable.TextViewAndSpaner_item_remark_textsize, 12);
item_remark_str.setTextSize(remarkSize);
}
}
private void setOption(TypedArray typeArray) {
isopposites = typeArray.getBoolean(
R.styleable.TextViewAndSpaner_is_item_option_opposites, false);
boolean isResult = typeArray.getBoolean(
R.styleable.TextViewAndSpaner_is_query_result, false);
if (isResult)
item_option.setEnabled(false);
else {
final CharSequence[] values = typeArray
.getTextArray(R.styleable.TextViewAndSpaner_item_option_values);
final String when_to_show_dialogue_item = typeArray
.getString(R.styleable.TextViewAndSpaner_when_to_show_dialogue_item);
final String prompt = typeArray
.getString(R.styleable.TextViewAndSpaner_item_option_prompt);
final ArrayAdapter<CharSequence> adapter = new ArrayAdapter<CharSequence>(
mContext, android.R.layout.simple_spinner_dropdown_item,
values);
final CharSequence[] attrs = typeArray
.getTextArray(R.styleable.TextViewAndSpaner_item_option_selected_dialogue_item);
final String promptStr = typeArray
.getString(R.styleable.TextViewAndSpaner_prompt_of_dialog);
item_option.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// TODO Auto-generated method stub
new AlertDialog.Builder(mContext)
.setTitle(prompt)
.setAdapter(adapter,
new DialogInterface.OnClickListener() {
public void onClick(
DialogInterface dialog,
int which) {
String selectedValue = values[which]
.toString();
item_option.setText(values[which]);
if (selectedValue
.equals(when_to_show_dialogue_item)) {
showDiag(attrs, promptStr);
} else {
item_option_ps.setText(null);
item_option_ps
.setVisibility(View.GONE);
}
dialog.dismiss();
}
}).create().show();
}
});
}
}
private void showDiag(final CharSequence[] attrs, String promptStr) {
AlertDialog.Builder builder = new Builder(mContext);
builder.setTitle("请选择……");
builder.setPositiveButton(attrs[0],
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
item_option_ps.setVisibility(View.VISIBLE);
item_option_ps.setText(attrs[0]);
}
});
builder.setNegativeButton(attrs[1],
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
item_option_ps.setVisibility(View.VISIBLE);
item_option_ps.setText(attrs[1]);
}
});
builder.setIcon(android.R.drawable.ic_dialog_info);
builder.setMessage(promptStr);
builder.show();
}
private void remarkDialog() {
final EditText remark_text = new EditText(mContext);
String oldRemark_text = (String) item_remark_str.getText();
if (!TextUtils.isEmpty(oldRemark_text)) {
remark_text.setText(oldRemark_text);
}
new AlertDialog.Builder(mContext).setTitle("添加备注")
.setIcon(android.R.drawable.ic_dialog_info)
.setView(remark_text)
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
item_remark_str.setVisibility(View.VISIBLE);
item_remark_str.setText(remark_text.getText()
.toString().trim());
}
}).setNegativeButton("取消", null).show();
}
public String getKeyValue() {
String optionResult = (String) item_option.getText();
if (isopposites) {
// 如果是反义,则在值后面加空格
return optionResult + " ";
}
return optionResult;
}
public void setOptionDefaultValue() {
item_option.setText("");
}
public String getRemarkValue() {
String psStr = (String) item_option_ps.getText();
String remarkStr = (String) item_remark_str.getText();
if (psStr != null && remarkStr != null
&& !TextUtils.isEmpty(psStr.trim())
&& !TextUtils.isEmpty(remarkStr.trim())) {
return item_content.getText() + "的备注:" + psStr + "," + remarkStr;
}
if (psStr != null && !TextUtils.isEmpty(psStr.trim())) {
return item_content.getText() + "的备注:" + psStr;
}
if (remarkStr != null && !TextUtils.isEmpty(remarkStr.trim())) {
return item_content.getText() + "的备注:" + remarkStr;
}
return null;
}
}
MainActivity.java
package com.yang;
import android.app.Activity;
import android.os.Bundle;
public class MainActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}
例子图示如下:
摩托罗拉xt910
中兴 v880
中兴 n760
华为c8500
二、现象说明
我们看到根据屏幕大小的不同,两行文字中下行文字的大小也随之改变,其中摩托罗拉xt910和中兴 v880下面字体的都比上面字体大,而中兴 n760下面字体与上面字体大小是相同的,而华为c8500下面字体比上面字体小。我们再来看看main.xml代码:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:test="http://schemas.android.com/apk/res/com.yang"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:background="@color/background">
<TextView android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:textColor="@android:color/black"
android:textSize="18sp"
android:text="@string/shiyan"/>
<com.yang.widget.TextViewAndSpaner
android:layout_width="fill_parent"
android:layout_height="wrap_content"
test:item_content_text="@string/shiyan"
test:item_content_textsize="18sp"
test:item_content_textcolor="@android:color/black"
test:item_option_prompt="@string/please_select"
test:item_option_values="@array/tureorfalse" />
</LinearLayout>
TextView设置的字体大小为18sp,而com.yang.widget.TextViewAndSpaner设置的字体大小也为18sp
com.yang.widget.TextViewAndSpaner读取字体大小的代码如下:
float contentSize = typeArray.getDimension(
R.styleable.TextViewAndSpaner_item_content_textsize, 15);
Logger.d("contentSize is " + contentSize);
四个手机打印出自定义属性来的字体大小为:
摩托罗拉xt910
09-08 15:07:00.685: D/CustomWidget(4937): contentSize is 27.0
中兴 v880
09-08 15:15:04.685: D/CustomWidget(4937): contentSize is 27.0
中兴 n760
09-08 15:05:15.640: D/CustomWidget(31487): contentSize is 18.0
华为c8500
09-08 15:22:48.565: D/CustomWidget(2018): contentSize is 13.5
三、注意事项
以上我们看到使用自定义属性的
typeArray.getDimension
是不靠谱的,字体的大小会根据不同屏幕的分辨率大小而改变字体的大小。我们再来看看关于getDimension
public float getDimension (int id)
Retrieve a dimensional for a particular resource ID. Unit conversions are based on the current DisplayMetrics
associated with the resources.
Parameters
id | The desired resource identifier, as generated by the aapt tool. This integer encodes the package, type, and resource entry. The value 0 is an invalid identifier. |
---|
Returns
- Resource dimension value multiplied by the appropriate metric.
Throws
Resources.NotFoundException | Throws NotFoundException if the given ID does not exist. |
---|
单位的转换是基于当前资源显示分比率的。
因此我们要慎重使用getDimension定义的属性,不然我们的应用部署到不同的应用上,原先非常美丽的效果,会变得面目全非。
四、解决方案
使用时,默认将字体大小设定好,而在xml配置文件当中删除关于字体大小的设置。如
删除main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:test="http://schemas.android.com/apk/res/com.yang"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:background="@color/background">
<TextView android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:textColor="@android:color/black"
android:textSize="18sp"
android:text="@string/shiyan"/>
<com.yang.widget.TextViewAndSpaner
android:layout_width="fill_parent"
android:layout_height="wrap_content"
test:item_content_text="@string/shiyan"
test:item_content_textsize="18sp"
test:item_content_textcolor="@android:color/black"
test:item_option_prompt="@string/please_select"
test:item_option_values="@array/tureorfalse" />
</LinearLayout>
中的
test:item_content_textsize="18sp"
配置,使其变成:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:test="http://schemas.android.com/apk/res/com.yang"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:background="@color/background">
<TextView android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:textColor="@android:color/black"
android:textSize="18sp"
android:text="@string/shiyan"/>
<com.yang.widget.TextViewAndSpaner
android:layout_width="fill_parent"
android:layout_height="wrap_content"
test:item_content_text="@string/shiyan"
test:item_content_textcolor="@android:color/black"
test:item_option_prompt="@string/please_select"
test:item_option_values="@array/tureorfalse" />
</LinearLayout>
同时在读取自定义属性的java代码中设置好自己想要的字体大小即可,如下:
float contentSize = typeArray.getDimension(
R.styleable.TextViewAndSpaner_item_content_textsize, 15);