2.3 简单控件
本节介绍Android几个简单控件的用法与注意点,主要包括文本视图TextView的跑马灯与聊天室效果、按钮Button的监听器使用、图像视图ImageView的拉伸效果与截图功能、图像按钮ImageButton的适用场合等。
2.3.1 文本视图TextView
TextView是最基础的文本显示控件,常用的基本属性和设置方法见表2-4。
表2-4 TextView的基本属性和设置方法说明
读者对于这些基本属性和方法想必并不陌生,因为在第1章第一个App“Hello World”中就用到了它们,这里不再赘述。接下来介绍TextView的两个特效用法。
1.跑马灯效果
当一行文本的内容太多,导致无法全部显示,也不想分行展示时,只能让文字从左向右滚动显示,类似于跑马灯。电视在播报突发新闻时经常在屏幕下方轮播消息文字,比如“快讯:我国选手***在刚刚结束的**比赛中为中国代表团夺得第**枚金牌”。
跑马灯效果在XML布局文件中实现时需要额外指定部分属性,这些特殊属性及其设置方法的详细说明见表2-5。
表2-5 跑马灯用到的属性与方法说明
表2-6 省略方式的取值说明
下面是演示跑马灯效果的XML布局文件:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:gravity="center" android:text="跑马灯效果,点击暂停,再点击恢复" /> <TextView android:id="@+id/tv_marquee" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:singleLine="true" android:ellipsize="marquee" android:focusable="true" android:focusableInTouchMode="true" android:textColor="#000000" android:textSize="17sp" android:text="快讯 :红色预警,超强台风“莫兰蒂”即将登陆,请居民关紧门窗、备足粮草,做好防汛救灾准备!" /> </LinearLayout>
跑马灯滚动的效果界面如图2-7和图2-8所示。左图为跑马灯文字在滚动中,右图为跑马灯文字停止滚动。
图2-7 跑马灯文字滚动界面
图2-8 跑马灯文字停止滚动界面
2.聊天室或者文字直播间效果
聊天室窗口的高度是固定的,新的文字消息总是加入窗口末尾,同时窗口内部的文本整体向上滚动,窗口的大小、位置保持不变。
在XML布局文件中实现聊天室时需要额外指定部分属性,这些特殊属性及其设置方法的详细说明见表2-7。
表12-7 聊天室用到的属性与方法说明
接下来看一个简单聊天室的例子,点击聊天室窗口可以添加一条聊天记录,长按聊天窗口可以清除所有聊天记录。聊天室的演示界面如图2-9和图2-10所示,图2-10比图2-9多添加了3条聊天记录,整个聊天记录的文字自动往上滚动。
图2-9 初始的聊天室界面
图2-10 增加了3条聊天记录
下面是聊天室例子用到的XML布局文件内容:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:id="@+id/tv_control" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:gravity="center" android:text="聊天室效果,点击添加聊天记录,长按删除聊天记录" /> <LinearLayout android:layout_width="match_parent" android:layout_height="200dp" android:orientation="vertical"> <TextView android:id="@+id/tv_bbs" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginTop="20dp" android:scrollbars="vertical" android:textColor="#000000" android:textSize="17sp"/> </LinearLayout> </LinearLayout>
下面是聊天室例子用到的代码示例:
public class BbsActivity extends AppCompatActivity implements OnClickListener, OnLongClickListener { private TextView tv_bbs; private TextView tv_control; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_bbs); tv_control = (TextView) findViewById(R.id.tv_control); tv_control.setOnClickListener(this); tv_control.setOnLongClickListener(this); tv_bbs = (TextView) findViewById(R.id.tv_bbs); tv_bbs.setOnClickListener(this); tv_bbs.setOnLongClickListener(this); tv_bbs.setGravity(Gravity.LEFT|Gravity.BOTTOM); tv_bbs.setLines(8); tv_bbs.setMaxLines(8); tv_bbs.setMovementMethod(new ScrollingMovementMethod()); } private String[] mChatStr = { "你吃饭了吗?", "今天天气真好呀。", "我中奖啦!", "我们去看电影吧", "晚上干什么好呢?", }; @Override public void onClick(View v) { if (v.getId() == R.id.tv_control || v.getId() == R.id.tv_bbs) { int random = (int)(Math.random()*10) % 5; String newStr = String.format("%s\n%s %s", tv_bbs.getText().toString(), DateUtil.getNowTime(), mChatStr[random]); tv_bbs.setText(newStr); } } @Override public boolean onLongClick(View v){ if(v.getId()==R.id.tv_control||v.getId()==R.id.tv_bbs){ tv_bbs.setText(""); } return true; } }
2.3.2 按钮Button
Button派生自TextView,二者在UI上的区别主要是Button控件有个按钮外观,提示用户点击这里。系统默认的按钮外观通常都不好看,需要更换靓一点、活泼一点的图片,这时在布局文件中修改Button节点的background属性就可以了。如果把background属性设置为@null,就会去除Button控件的背景样式,此时的Button看起来跟TextView没什么区别。
前面在演示聊天室功能时,我们为TextView引入了点击方法和长按方法。因为点击和长按监听器都来源于View类,所以这两个方法及其监听器并非Button特有的,而是所有布局和控件都能使用的,一般用于为按钮控件注册点击和长按事件。
Android中的简单按钮主要是Button和后面提到的ImageButton。这两个按钮对点击和长按监听器的使用方法并不复杂,主要步骤如下:
步骤01 自己定义一个扩展自监听器的类,如点击监听器扩展自View.OnClickListener,长按监听器扩展自View.OnLongClickListener。为了方便起见,也可以直接给页面的Activity类加上监听器接口。
步骤02 在自定义监听器类中重写点击或者长按方法,加入事件处理的代码。点击方法的名称是onClick,长按方法的名称是onLongClick。
步骤03 哪个视图要响应点击或长按,就给哪个视图注册对应的监听器对象。点击事件的注册方法是setOnClickListener,长按事件的注册方法是setOnLongClickListener。
下面是给Button对象注册点击监听器和长按监听器的代码:
public class ClickActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_click); Button btn_click = (Button) findViewById(R.id.btn_click); btn_click.setOnClickListener(new MyOnClickListener()); btn_click.setOnLongClickListener(new MyOnLongClickListener()); } class MyOnClickListener implements View.OnClickListener { @Override public void onClick(View v) { if (v.getId() == R.id.btn_click) { Toast.makeText(ClickActivity.this, "您点击了控件 :"+((TextView)v).getText(),Toast.LENGTH_SHORT).show(); } } } class MyOnLongClickListener implements View.OnLongClickListener { @Override public boolean onLongClick(View v) { if (v.getId() == R.id.btn_click) { Toast.makeText(ClickActivity.this, "您长按了控件 :"+((TextView)v).getText(),Toast.LENGTH_SHORT).show(); } return true; } } }
2.3.3 图像视图ImageView
ImageView是图像显示控件,与图形显示有关的属性说明如下。
● scaleType:指定图形的拉伸类型,默认是fitCenter。拉伸类型的取值说明见表2-8。
表2-8 拉伸类型的取值说明
● src:指定图形来源,src图形按照scaleType拉伸。注意背景图不按scaleType指定的方式拉伸,背景默认以fitXY方式拉伸。
ImageView在代码中调用的方法说明如下。
● setScaleType:设置图形的拉伸类型。具体的取值说明见表2-8。
● setImageDrawable:设置图形的Drawable对象。
● setImageResource:设置图形的资源ID。
● setImageBitmap:设置图形的位图对象。
读者应该注意到ImageView的拉伸类型种类繁多、文字说明不易理解,特别是center相关的类型就有4种:fitCenter、center、centerCrop、centerInside。接下来进行一个实验,把一张图片放入ImageView控件,尝试使用不同的拉伸类型,看看效果有什么区别。下面是图片拉伸演示用的代码示例:
public class ScaleActivity extends AppCompatActivity implements OnClickListener { private ImageView iv_scale; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_scale); iv_scale = (ImageView) findViewById(R.id.iv_scale); findViewById(R.id.btn_center).setOnClickListener(this); findViewById(R.id.btn_fitCenter).setOnClickListener(this); findViewById(R.id.btn_centerCrop).setOnClickListener(this); findViewById(R.id.btn_centerInside).setOnClickListener(this); findViewById(R.id.btn_fitXY).setOnClickListener(this); findViewById(R.id.btn_fitStart).setOnClickListener(this); findViewById(R.id.btn_fitEnd).setOnClickListener(this); } @Override public void onClick(View v) { if (v.getId() == R.id.btn_center) { iv_scale.setScaleType(ImageView.ScaleType.CENTER); } else if (v.getId() == R.id.btn_fitCenter) { iv_scale.setScaleType(ImageView.ScaleType.FIT_CENTER); } else if (v.getId() == R.id.btn_centerCrop) { iv_scale.setScaleType(ImageView.ScaleType.CENTER_CROP); } else if (v.getId() == R.id.btn_centerInside) { iv_scale.setScaleType(ImageView.ScaleType.CENTER_INSIDE); } else if (v.getId() == R.id.btn_fitXY) { iv_scale.setScaleType(ImageView.ScaleType.FIT_XY); } else if (v.getId() == R.id.btn_fitStart) { iv_scale.setScaleType(ImageView.ScaleType.FIT_START); } else if (v.getId() == R.id.btn_fitEnd) { iv_scale.setScaleType(ImageView.ScaleType.FIT_END); } } }
至于图像拉伸的演示界面,fitCenter的效果如图2-11所示,图片被拉伸但未超出控件范围;center的效果如图2-12所示,图片没有拉伸;centerCrop的效果如图2-13所示,图片被拉伸且已超出控件范围;centerInside的效果如图2-14所示,图片没有被拉伸。
图2-11 fitCenter的效果图
图2-12 center的效果图
图2-13 centerCrop的效果图
图2-14 centerInside的效果图
Android能用ImageView展示图片,也自带屏幕截图功能。尽管自带的屏蔽截图功能有些简单,不过多数场合已经够用了。截图功能面向所有视图,我们可以从其他控件或布局那里截图下来,然后显示在ImageView上面。
使用截图功能必须通过代码完成,相关方法如下(这些方法都来自于View类)。
● setDrawingCacheEnabled:设置绘图缓存的可用状态。true表示打开,false表示关闭。
● isDrawingCacheEnabled:判断该控件的绘图缓存是否可用。
● setDrawingCacheQuality:设置绘图缓存的质量。
● getDrawingCache:获取该控件的绘图缓存结果,返回值为Bitmap类型。
● setDrawingCacheBackgroundColor:设置绘图缓存的背景颜色。大家可能会奇怪为何要提供该方法,因为绘图缓存默认背景色是黑色,如果不提前设置缓存的背景色,截图的结果就是黑乎乎一片,所以需要将背景色设置为默认颜色(通常是白色)。
操作截图功能的具体步骤如下:
步骤01 开始截图前,先调用setDrawingCacheEnabled方法,设置绘图缓存为可用状态。注意该方法在一开始就得调用,因为先开启绘图缓存,之后变更的界面才会记录到缓存中;如果先变更界面再开启绘图缓存,缓存里就是空的。
步骤02 调用getDrawingCache方法获取缓存中的图像数据。
步骤03 完成截图,延迟若干毫秒后调用setDrawingCacheEnabled方法关闭绘图缓存。如果接下来还要截图,就再次调用setDrawingCacheEnabled方法重新开启绘图缓存。
下面是完成截图功能的代码:
public class CaptureActivity extends AppCompatActivity implements OnClickListener, OnLongClickListener { private TextView tv_capture; private ImageView iv_capture; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_capture); tv_capture = (TextView) findViewById(R.id.tv_capture); iv_capture = (ImageView) findViewById(R.id.iv_capture); tv_capture.setDrawingCacheEnabled(true); findViewById(R.id.btn_chat).setOnClickListener(this); findViewById(R.id.btn_chat).setOnLongClickListener(this); findViewById(R.id.btn_capture).setOnClickListener(this); } private String[] mChatStr = { "你吃饭了吗?", "今天天气真好呀。", "我中奖啦!", "我们去看电影吧。", "晚上干什么好呢?" }; @Override public boolean onLongClick(View v) { if (v.getId() == R.id.btn_chat) { tv_capture.setText(""); } return true; } @Override public void onClick(View v) { if (v.getId() == R.id.btn_chat) { int random = (int)(Math.random()*10) % 5; String newStr = String.format("%s\n%s %s", tv_capture.getText().toString(), DateUtil.getNowTime(), mChatStr[random]); tv_capture.setText(newStr); } else if (v.getId() == R.id.btn_capture) { Bitmap bitmap = tv_capture.getDrawingCache(); iv_capture.setImageBitmap(bitmap); // 注意截图完毕后不能马上关闭绘图缓存,因为界面渲染需要时间 // 如果立即关闭缓存,渲染界面就会找不到位图对象,从而报错 // java.lang.IllegalArgumentException:Cannot draw recycled bitmaps mHandler.postDelayed(mResetCache, 200); } } private Handler mHandler = new Handler(); private Runnable mResetCache = new Runnable() { @Override public void run() { tv_capture.setDrawingCacheEnabled(false); tv_capture.setDrawingCacheEnabled(true); } }; }
对应的截图演示界面如图2-15和图2-16所示。其中,图2-15所示为截图前的界面,图2-16所示为截图后的界面。
图2-15 截图前只有左边有文字
图2-16 截图后在右边显示图片
2.3.4 图像按钮ImageButton
ImageButton其实派生自ImageView,而不是派生自Button, ImageView拥有的属性和方法,ImageButton统统拥有,只是ImageButton有个默认的按钮外观。
ImageButton和Button都起到控制按钮的作用,不同的是Button是文本按钮,ImageButton是图像按钮,这两个按钮的主要区别在于:
(1)Button既可显示文本也可显示图形(通过设置背景图),而ImageButton只能显示图形不能显示文本。
(2)ImageButton上的图像可按比例拉伸,而Button上的大图会拉伸变形(因为背景图无法按比例拉伸)。
(3)Button只能在背景显示一张图形,而ImageButton可分别在前景和背景显示两张图形,实现图片叠加的效果。
从上面可以看出,Button与ImageButton各有千秋,通常情况下使用Button就够用了。但在某些场合,比如输入法打不出来的字符和以特殊字体显示的字符串,就适合先切图再用ImageButton显示。
现在我们有了Button可在按钮上显示文字,又有ImageButton可在按钮上显示图形,照理说绝大多数场合都够用了。可是现实项目中的需求往往十分怪异,例如客户要求在按钮文字的左边加一个图标,这样按钮内部既有文字又有图片,乍看之下Button和ImageButton都没法直接使用。若把图标和文字放在一起切图,每次图标与文字的大小或距离发生变化时岂不是都要重新切图?若用LinearLayout对ImageView和TextView组合布局,这样固然可行,但是布局文件会冗长许多。
其实有个既简单又灵活的办法,要想在文字周围放置图片,使用TextView就能实现,那么基于TextView的Button自然能实现。具体可在XML布局文件中设置以下5个属性。
● drawableTop:指定文本上方的图形。
● drawableBottom:指定文本下方的图形。
● drawableLeft:指定文本左边的图形。
● drawableRight:指定文本右边的图形。
● drawablePadding:指定图形与文本的间距。
若在代码中实现,则可调用如下方法。
● setCompoundDrawables:设置文本周围的图形。可分别设置左边、上边、右边、下边的图形。
● setCompoundDrawablePadding:设置图形与文本的间距。
下面的代码演示在按钮中变换图标位置的功能:
public class IconActivity extends AppCompatActivity implements OnClickListener { private Button btn_icon; private Drawable drawable; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_icon); btn_icon = (Button) findViewById(R.id.btn_icon); drawable = getResources().getDrawable(R.mipmap.ic_launcher); // 必须设置图片大小,否则不显示图片 drawable.setBounds(0, 0, drawable.getMinimumWidth(), drawable.getMinimumHeight()); findViewById(R.id.btn_left).setOnClickListener(this); findViewById(R.id.btn_top).setOnClickListener(this); findViewById(R.id.btn_right).setOnClickListener(this); findViewById(R.id.btn_bottom).setOnClickListener(this); } @Override public void onClick(View v) { if (v.getId() == R.id.btn_left) { btn_icon.setCompoundDrawables(drawable, null, null, null); } else if (v.getId() == R.id.btn_top) { btn_icon.setCompoundDrawables(null, drawable, null, null); } else if (v.getId() == R.id.btn_right) { btn_icon.setCompoundDrawables(null, null, drawable, null); } else if (v.getId() == R.id.btn_bottom) { btn_icon.setCompoundDrawables(null, null, null, drawable); } } }
变换图标位置的效果界面如图2-17(图标在文字左边)、图2-18(图标在文字右边)、图2-19(图标在文字上边)、图2-20(图标在文字下边)所示。
图2-17 图标在文字左边
图2-18 图标在文字右边
图2-19 图标在文字上边
图2-20 图标在文字下边