谈到Android中与文本相关的控件,容易想到的自然是TextView和EditText。这两类控件的使用率很高,基本使用方法也很简单。但是如果追寻细节,会发现它们还隐藏着很多的黑科技知识。本文的内容包括:TextView、EditText、AutoCompleteTextView、MultiAutoCompleteTextView。
TextView是很常用的控件,下文主要介绍一下有用的属性:
[bold|italic|normal]
,对应[加粗、斜体、正常]
。...
出现的位置。可选值为[none|start|end|middle|marquee]
,对应[无省略符、开头、结尾、中间、跑马灯模式]
。当选择marquee时需要配合singleLine使用。[none|all|email|phone|web|map]
,对应[不识别、全部识别、邮箱、电话号码、网址、地图]
。对于HTML格式的字符串,需要先使用Html
的静态方法fromHtml
将字符串转换为Spanned
对象,再将该对象设置给TextView,示例代码如下:
String htmlStr="这是一段测试文字。用于测试TextView对HTML的支持。如下:<a href='https://www.baidu.com'>百度一下a>";
Spanned htmlSpanned=Html.fromHtml(htmlStr);
htmlStrTextView.setText(htmlSpanned);
注意,如果想让文本中的超链接生效(点击打开浏览器),还需要为TextView设置LinkMovementMethod
,如下:
htmlStrTextView.setMovementMethod(new LinkMovementMethod());
在某些情况下,一段文本的不同区域可能要采取不同的样式,那么就需要Spannable
的帮助。关于Spannable的使用,主要分为三步:
setSpan
方法为Spannable对象设置Span(可以设置多次)setSpan的方法原型如下:
//what:需要设置的span(这一系列类都以Span结尾)
//start:span作用范围的开始位置
//end:span作用范围的结束位置
//flags:标识作用范围是否包含start和end所在的位置(一般选择Spanned.SPAN_INCLUSIVE_EXCLUSIVE)
public void setSpan(Object what, int start, int end, int flags);
示例代码:
Spannable testSpannable=new SpannableString("这是一段测试文字。用于测试TextView对Spannable的支持。包括背景色、前景色、下划线区域、链接、点击事件等");
testSpannable.setSpan(new BackgroundColorSpan(Color.BLUE),
37,40,Spanned.SPAN_INCLUSIVE_EXCLUSIVE);//设置背景色
testSpannable.setSpan(new ForegroundColorSpan(Color.RED),
41,44,Spanned.SPAN_INCLUSIVE_EXCLUSIVE);//设置前景色
testSpannable.setSpan(new UnderlineSpan(),
45,50,Spanned.SPAN_INCLUSIVE_EXCLUSIVE);//设置下划线
testSpannable.setSpan(new URLSpan("http://blog.csdn.net/codingending"),
51,53,Spanned.SPAN_INCLUSIVE_EXCLUSIVE);//设置超链接
testSpannable.setSpan(new ClickableSpan() {
@Override
public void onClick(View widget) {
Toast.makeText(widget.getContext(),"文本内点击",Toast.LENGTH_SHORT).show();
}
},54,58,Spanned.SPAN_INCLUSIVE_EXCLUSIVE);//设置点击事件
spannableTextView.setText(testSpannable);
spannableTextView.setMovementMethod(new LinkMovementMethod());//支持超链接和点击事件
效果截图:
可以使用的span都在android.text.style
下,下面列出一些常见的span:
在TextView内容过多的时候,我们可能希望TextView是可滑动的,那么只需要完成以下两步即可:
1.在XML中设置scrollbars属性:
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="vertical"/>
2.在Java中为TextView设置MovementMethod
textView.setMovementMethod(ScrollingMovementMethod.getInstance());
EditText是TextView的子类,所以TextView有的属性EditText同样也有,这里仅仅介绍一些上文没有提到的属性:
在本例中,将演示如何通过改变EditText的默认样式,最终的效果如下:
首先,准备一张图片作为EditText的drawableLeft
资源,并放入对应的drawable文件夹下。在本例中命名为ic_account_box.png
。
然后,在drawable文件下新建两个shape
资源,分别作为EditText在获得焦点和失去焦点时的下划线。在本例中命名为link_pink.xml
和link_blue.xml
。
link_pink.xml:
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<size android:height="2dp" android:width="500dp"/>
<solid android:color="#FF4081"/>
shape>
link_blue.xml:
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<size android:height="2dp" android:width="500dp"/>
<solid android:color="#3F51B5"/>
shape>
接着,在drawable文件夹下新建一个selector
资源,作为EditText的drawableBottom
资源,在本例中命名为edit_text_bottom_drawable.xml
:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="true"
android:drawable="@drawable/line_pink"/>
<item android:state_focused="false"
android:drawable="@drawable/line_blue"/>
<item android:state_focused="true"
android:drawable="@drawable/line_blue"/>
selector>
最后,将EditText的background
设置为null,应用各个drawable资源并通过drawablePadding
属性设置图片和文字的间距,代码如下:
"match_parent"
android:layout_height="wrap_content"
android:background="@null"
android:drawableLeft="@drawable/ic_account_box"
android:drawableBottom="@drawable/edit_text_bottom_drawable"
android:drawablePadding="2dp"
android:hint="自定义样式的EditText" />
通过为EditText设置监听器,可以在文本内容发生改变时触发相应的回调方法。
//监听用户的输入
watchWriteEditText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
Log.i(TAG,"beforeTextChanged:"+s);//内容改变前
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
Log.i(TAG,"onTextChanged:"+s);//内容改变时
}
@Override
public void afterTextChanged(Editable s) {
Log.i(TAG,"afterTextChanged:"+s);//内容改变后
}
});
1.使用android:inputType属性指定输入内容的格式:
通过为EditText设置android:inputType
属性,可以指定输入内容的格式,如邮箱、数字、密码等。同时,输入法也会呈现最合适的输入界面。这个属性的可选值有很多,下面列出其中一部分:
2.使用android:digits属性指定能够输入的字符:
如果想要限定EditText能够输入的字符,只需要设置android:digits
属性即可,示例代码如下:
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:digits="0123456789*#"
android:hint="只能输入0123456789*#" />
3.使用InputFilter限制输入内容
如果需要实现的内容限制规则比较复杂,就需要代码的配合了。简单来说,要定义自己的InterFilter
子类并重写filter
方法。示例代码如下:
//通过代码限制输入内容只能为数字
InputFilter filter=new InputFilter() {
@Override
public CharSequence filter(CharSequence source, int start, int end,
Spanned dest, int dstart, int dend) {
if(!TextUtils.isDigitsOnly(source)){
return "";//如果当前输入的内容包含数字之外的字符,就默认输入为空字符
}
return source;
}
};
codeLimitInputEditText.setFilters(new InputFilter[]{filter});//可以设置多个InputFilter
setFilters
方法的参数是一个数组,因此我们可以为同一个EditText设置多个InputFilter。
filter
方法的参数含义如下:
在虚拟键盘的右下角有一个默认的回车键按钮,通过设置EditText的imeOptions
属性,可以自定义这个按钮。
imeOptions的可选值有多个,下面列出常见的几种及其含义:
首先,为EditText设置android:inputType
和android:imeOptions
属性,这两者配合才会生效。示例代码如下:
<EditText
android:id="@+id/edit_text_style_ime_option"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text"
android:imeOptions="actionGo"
android:hint="自定义右下角回车键" />
然后,在代码中为EditText设置OnEditorActionListener
,通过判断actionId
为自定义回车键指定相应的动作。示例代码如下:
//自定义虚拟键盘右下角的行为
styleOmeOptionEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if(actionId== EditorInfo.IME_ACTION_SEARCH){
Toast.makeText(v.getContext(),"点击了搜索按钮",Toast.LENGTH_SHORT).show();
return true;
}
return false;
}
});
最终的演示效果截图:
AutoCompleteTextView是具有输入提示功能的输入控件,属于EditText的直接子类。
与EditText相关的属性AutoCompleteTextView同样也有,因此这里仅仅列出一些其特有的属性:
XML代码:
<AutoCompleteTextView
android:id="@+id/auto_complete_text_view_normal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:completionThreshold="1"
android:hint="请输入关键词" />
设置Adapter:
String[] dataResArray={"coding","ending","codingending","coding and ending"};
ArrayAdapter<String> normalAdapter=new ArrayAdapter<String>(getActivity(),
android.R.layout.simple_list_item_1,dataResArray);
normalAutoCompleteView.setAdapter(normalAdapter);
效果截图:
某些情况下,需要展示复杂的数据(比如自定义的对象),使用系统的ArrayAdapter并不能满足需求。我们就需要自定义Adapter,并设置相应的过滤逻辑。大致步骤如下:
BookAdapter
),并实现相应的方法(getCount
、getItem
、getItemId
、getView
),以实现自定义的下拉列表样式。BookAdapter
实现Filterable
接口,并实现getFilter
方法,保证可以对列表内容进行过滤。Filter
对象(过滤器),我们还需要定义一个Filter的子类(在本例中为BookFilter
),并实现performFiltering
和publishResults
方法。为了方便操作,可以将BookFilter定义为BookAdapter的内部类。实体类:
public class Book {
private String name;
private int imageRes;//图片资源
public Book(String name, int imageRes) {
this.name = name;
this.imageRes = imageRes;
}
@Override
public String toString() {//重写toString方法,保证选择下拉提示列表中的某一项时返回书名
return name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getImageRes() {
return imageRes;
}
public void setImageRes(int imageRes) {
this.imageRes = imageRes;
}
}
自定义Adapter和Filter:
public class StyleFilterAdapter extends BaseAdapter implements Filterable{
private Context context;
private List dataList;//数据源
private List originDataList;//保存原始数据
private BookFilter bookFilter;//过滤器
private final Object lock=new Object();//同步锁(为了在进行数据复制时保证线程安全)
public StyleFilterAdapter(Context context, List dataList) {
this.context = context;
this.dataList = dataList;
}
@Override
public int getCount() {
return dataList.size();
}
@Override
public Object getItem(int position) {
return dataList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder=null;
if(convertView==null){
LayoutInflater inflater=LayoutInflater.from(context);
convertView=inflater.inflate(R.layout.auto_complete_view_item,parent,false);
holder=new ViewHolder();
holder.bookImage=convertView.findViewById(R.id.book_image);
holder.bookNameView=convertView.findViewById(R.id.book_name);
convertView.setTag(holder);
}else{
holder= (ViewHolder) convertView.getTag();
}
Book book=dataList.get(position);
holder.bookImage.setImageResource(book.getImageRes());
holder.bookNameView.setText(book.getName());
return convertView;
}
static class ViewHolder{//View复用机制
ImageView bookImage;
TextView bookNameView;
}
@Override
public Filter getFilter() {
if(bookFilter==null){
bookFilter=new BookFilter();
}
return bookFilter;
}
//用于筛选Book列表的过滤器
class BookFilter extends Filter{
@Override
protected FilterResults performFiltering(CharSequence constraint) {//指定过滤逻辑
FilterResults results=new FilterResults();
if(originDataList==null){//为原始数据列表赋值
synchronized (lock){//添加同步锁
originDataList=new ArrayList<>(dataList);
}
}
if(TextUtils.isEmpty(constraint)){//如果筛选条件为空直接返回原始数据列表的副本
synchronized (lock){//添加同步锁
List tempList=new ArrayList<>(originDataList);
results.values=tempList;
results.count=tempList.size();
}
}else{
List tempList=new ArrayList<>(originDataList.size());//保存已筛选的数据
for(Book book:originDataList){
if(book.getName().contains(constraint)){//保存包含关键字的数据
tempList.add(book);
}
}
results.values=tempList;
results.count=tempList.size();
}
return results;
}
@Override
protected void publishResults(CharSequence constraint,
FilterResults results) {//将过滤结果反馈给Adapter
dataList= (List) results.values;
if(results.count>0){
notifyDataSetChanged();//刷新数据
}else{
notifyDataSetInvalidated();//重绘控件
}
}
}
}
在代码中使用:
final List bookList=new ArrayList<>();
bookList.add(new Book("《小王子》",R.mipmap.ic_launcher));
bookList.add(new Book("《狮子王》",R.mipmap.ic_launcher));
bookList.add(new Book("《王小波全集》",R.mipmap.ic_launcher));
final StyleFilterAdapter styleAdapter=new StyleFilterAdapter(getActivity(),bookList);
styleAutoCompleteView.setAdapter(styleAdapter);
默认情况下,即使将android:completionThreshold
属性设置为0,AutoCompleteTextView也不会在已输入内容为空时弹出下拉提示列表。下面介绍一种简单的解决方案:
styleAutoCompleteView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if(hasFocus){//保证在没有输入内容的情况下(但是有焦点)也能获得自动补全的提示
styleAutoCompleteView.showDropDown();//展示下拉列表(会根据内容自动筛选)
}
}
});
逻辑很简单,就是在AutoCompleteTextView获得焦点时通过代码显示下拉列表,此时会自动根据已输入的内容进行对数据源进行筛选。
效果截图:
MultiAutoCompleteTextView是AutoCompleteTextView的直接子类,用法也几乎一致。区别在于,后者只能获得一次输入提示(一旦选择某一项就不再提示了),但是MultiAutoCompleteTextView通过分隔符可以获得多次输入提示。
XML代码:
<MultiAutoCompleteTextView
android:id="@+id/multi_auto_complete_text_view_style"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:completionThreshold="1"
android:hint="请输入关键字"/>
设置Adapter和分隔符:
String[] dataResArray={"coding","ending","codingending","coding and ending"};
ArrayAdapter<String> multiDataAdapter=new ArrayAdapter<String>(getActivity(),
android.R.layout.simple_list_item_1,dataResArray);
multiAutoCompleteTextView.setAdapter(multiDataAdapter);
//设置分隔符
multiAutoCompleteTextView.setTokenizer(new MultiAutoCompleteTextView.CommaTokenizer());
CommaTokenizer
是预设的分隔符对象,默认为英文逗号和若干个空格。
除了使用系统提供的CommaTokenizer
,我们也可以定义自己的分隔符对象,只需要实现MultiAutoCompleteTextView.Tokenizer
接口即可(大部分代码都可以参照CommaTokenizer的内容)。示例代码如下:
自定义的分隔符类:
public class StyleTokenizer implements MultiAutoCompleteTextView.Tokenizer{
private char myTokenizer;//分隔符
public StyleTokenizer(char myTokenizer) {
this.myTokenizer = myTokenizer;
}
public int findTokenStart(CharSequence text, int cursor) {
int i = cursor;
while (i > 0 && text.charAt(i - 1) != myTokenizer) {
i--;
}
while (i < cursor && text.charAt(i) == ' ') {
i++;
}
return i;
}
public int findTokenEnd(CharSequence text, int cursor) {
int i = cursor;
int len = text.length();
while (i < len) {
if (text.charAt(i) == myTokenizer) {
return i;
} else {
i++;
}
}
return len;
}
public CharSequence terminateToken(CharSequence text) {
int i = text.length();
while (i > 0 && text.charAt(i - 1) == ' ') {
i--;
}
if (i > 0 && text.charAt(i - 1) == myTokenizer) {
return text;
} else {
if (text instanceof Spanned) {
SpannableString sp = new SpannableString(text + String.valueOf(myTokenizer));
TextUtils.copySpansFrom((Spanned) text, 0, text.length(),
Object.class, sp, 0);
return sp;
} else {
return text + String.valueOf(myTokenizer);
}
}
}
}
在代码中使用:
......
//设置自定义分隔符(#)
styleMultiAutoCompleteView.setTokenizer(new StyleTokenizer('#'));
效果截图:
最终的演示效果截图:
《 Android UI 常用控件讲解》:包括CheckBox、RadioButton、ToggleButton、Switch、ProgressBar、SeekBar、RatingBar、Spinner、ImageButton。
https://github.com/CodingEnding/UISystemDemo [ 持续更新中 ]
https://www.jianshu.com/p/4e7d7a08fc7e
http://blog.csdn.net/u013699756/article/details/52294190
https://www.jianshu.com/p/2e9d54d761e7
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0120/2335.html
https://www.jianshu.com/p/5fe19793dd82
http://blog.csdn.net/u012702547/article/details/48708755
http://blog.csdn.net/a_long_/article/details/51011388