Android Studio开发实战:从零基础到App上线 (移动开发丛书)
上QQ阅读APP看书,第一时间看更新

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 图标在文字下边