layout: post
title: 安卓开发(一)环境搭建、基本程序与控件
description: 安卓开发(一)环境搭建、基本程序与控件
tag: 安卓
根据此Intent的描述,负责找到对应的组件,将 Intent传递给调用的组件,并完成组件的调用。
Intent不仅可用于应用程序之间,也可用于应用程序内部的Activity/Service之间的交互。因此,可以将Intent理解为不同组件之间通信的“媒介”专门提供组件互相调用的相关信息。
也可以在manifest.xml中activity中的action来实现跳转:
当xml配置文件中action名有重名时,会由用户选择执行哪一个。
使用Bundle 存储数据,再将bundle传到intent中(bundle.putExtras()):
在要切换到的activity中通过getIntent.getExtras(),获取到传来的bundle,取出内容:
这种的activity之间传参是双向的:
具体实现在发送方主要是:
接收方接收到信息进行处理,反馈数据给发送方
发送方接收到反馈信息后,确认请求码和接收码,编写回调。
整个过程类似http通信。
fragment类似vue中封装的小组件。
1-在activity中使用fragment占位,(需要注意填写fragment的name属性,指明fragment所在位置)
2-编写fragment具体的layout.xml文件
3-编写fragment对应的java类,在onCreatView中采用inflater填充器将fragment的layout文件填充到View
直接编写好fragment的layout文件和java类文件,然后在activity中
使用fragment管理器:在main_activity中:
Activity在onCreate()中对fragment进行了渲染加载,因此可以直接在onStart()中直接获取fragment中的元素来处理事件。
一般在Fragment的onActivityCreate中获取到所在Activity
注意:findViewById方法只在activity中可以使用
在fragment中onCreateView先填充view:
然后view有findViewById方法
一般来讲,最好的事件处理方法是fragment各自监听本身的控件,监听到事件后,把事件统一交给activity处理,
因此需要在fragment与activity中设置接口,传递事件。
单纯考虑fragment自己的监听和回调,步骤即为设置监听,以及监听到事件后回调执行。
而现在的做法是fragment中监听,监听到事件后在activity中执行,因此首先在fragment中定义一个接口interface来包裹fragment中对事件的监听,即抽象出fragment层的监听,该抽象也需要定义设定,而interface就是桥梁,interface在activity中实现,activity在onAttachFragment即装载fragment时,如遇到有监听的fragment,则将该fragment对事件的监听收归自己所有,具体而言就是通过设定interface将自身引入传用,执行监听到事件后的回调操作,至此完成activity对fragment中监听事件的通信。
这种做法的好处在于,在fragment中完成监听以后,可以交给不同的activity来复用,interface可以看做是对fragment的监听功能的一种封装打包,将其打包给activity使用。
步骤详解:
在fragment中定义监听事件接口和设置监听事件(“定义监听事件接口”,具体是指监听的执行,即监听谁,而设定监听事件,则是完成对监听事件的传出设置)设定接口方法,设定接口方法可将接口中传递的事件私有化到activity本身。
在activity中实现该接口(即,监听到事件后对事件的处理):
在fragment中进行设置监听(依旧是在onActivityCreate()方法下)
在onAttachFragment()中,即给activity装载fragment时,判断是否装载的是所需的fragment,如何是,将该fragment实例化,并实例化设置接口方法。
这里要注意,前边在fragment中只是定义了接口的设置,这里才是真正调用了所定义的方法,完成了接口对事件的实例化。因为Activity中实现了fragment的接口方法,所有activity本身即是监听事件。
activity获取了fragment的对事件的监听,执行回调。
项目开发的过程中,可能会大量的使用全局变量,在android开发中,大多数人更偏向于使用application来保存全局变量。Application类是用来维护应用程序全局状态。我们可以提供自己的实现,并在AndroidManifest.xml文件的标签中指出它的名字,这将导致在创建应用程序时去实例化我们自己的Application类。Android系统会为每个程序运行时创建一个Application类的对象且仅创建一个,所以Application可以说是单例模式的一个类。且Application对象的生命周期是整个程序中最长的,它的生命周期就等于这个程序的生命周期。因为它是全局唯一的,所以在不同的Activity,Service中获得的对象都是同一个对象。所以,通过Application来进行一些数据传递、数据共享、数据缓存等操作。
首先创建继承自Application的MyApplication类,定义变量score,并创建get和set方法。
复制代码
package com.example.whs.myapplication;
import android.app.Application;
/**
* Created by whs on 17/5/1.
*/
public class MyApplication extends Application {
private int score = 100;
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
}
下面就是如何使用我们创建的全局变量了,在MainActivity中,首先要获得MyApplication的对象,因为MyApplication继承自Application,所以使用getApplication()方法即可,然后可以调用MyApplication的get或set方法进行访问全局变量。
MyApplication application = (MyApplication)this.getApplication();
application.setScore(200);
int score = application.getScore()
手机端:查看手机安卓版本,比如安卓11.0,开启开发者模式(一般是系统管理、关于手机、版本信息、软件版本),进入开发者选项(进入开发者模式后设置中出现),打开USB调试。
电脑端:根据手机安卓版本,下载对应的SDK平台(设置中)
一般还需要给手机下载谷歌的USB驱动,在SDKtools中:
根据下载的位置:D:\Jetbrains\andriodSDK\extras\google\usb_driver,更新驱动程序。
生成apk
布局渲染的过程:
获取填充器,构建布局View,编辑View属性
LayoutInflater inflater = getLaoutInflater();
View layout = inflater.inflate(R.layout.custom,(ViewGroup)findViewById(R.id.llToast));
TextView title = layout.findViewById(R.id.tvTitleToast);
image.setImageResource(R.drawble.picture);
按钮的快速绑定三部曲:
1- 在主类中实现监听方法
2- 设置按钮监听器
3- 重写onClick()按钮点击的回调。
ImageView具有setImageDrawable()和getDrawable()来设置图像内容或者获取图像内容。
设置水平进度条:
<ProgressBar
android:id="@+id/id_progress"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
// 设置进度条
mProgressBar.setProgress(mProgress);
<RadioGroup
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="20dp"
android:id="@+id/rd_mode_set"
android:orientation="horizontal">
<RadioButton
android:id="@+id/bt_set_mode_normal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/bt_set_mode_normal" />
<RadioButton
android:id="@+id/bt_set_mode_sos"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/bt_set_mode_sos" />
<RadioButton
android:id="@+id/bt_set_mode_extreme"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/bt_set_mode_extreme" />
RadioGroup>
rg_mode_set = findViewById(R.id.rd_mode_set);
rg_mode_set.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
@Override
// 第一个参数 按钮组,第二个被选中按钮的id
public void onCheckedChanged(RadioGroup radioGroup, int i) {
for (Map.Entry<String, Integer> entry : MODE_MAP.entrySet()) {
if (Objects.equals(entry.getValue(), i)) {
try {
String cmd = getCMDDownMode('1',Integer.parseInt(entry.getKey()));
writeCh.setValue(cmd.getBytes("GBK"));
gatt.writeCharacteristic(writeCh);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
});
/**
* 修改头像选择
*/
private void selectImg(){
AlertDialog.Builder builder = new AlertDialog.Builder(this)
.setTitle("提示")
.setMessage("请选择操作类型")
.setPositiveButton("拍照", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
try {
Intent intent = new Intent(ActivityUser.this,TakePhoto.class);
startActivityForResult(intent, 12);
}catch (Exception e){
e.printStackTrace();
}
}
})
.setNegativeButton("图库", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
addPic();
}
})
.setNeutralButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
}
});
builder.create().show();
}
basic activity:
布局构成:
工具栏+content+浮动按钮
content使用的include引入的另一个layout资源文件:
具体的菜单选项内容在menu文件夹下定义。
orderInCategory,是设定选项的顺序。
可以添加icon图标
menu的每个item选项可以再嵌套menu:
在java类中,先在onCreateOptionsMenu()中使用getMenuInflater()对菜单进行填充
在onOptionsItemSelected()中设置菜单选项点击时的回调。
当然构建menu时也可以为每个setting设置onClick事件
如果有设置,则系统优先执行的是你设置的onClick,而不是onOptionsItemSelected()中的回调。
在activity中实现视图点击监听和弹出菜单点击监听。
创建PopupMenu对象,填充菜单内容,设置监听,在onMenuItemClick中写回调。
浮动模式
菜单可以动态添加
menu.add()
创建actionMode对象:
先判断actionMode是否已经执行,避免多次生成;
然后设置
startActionMode
需要传递actionModeCallback参数,就是执行actionMode的回调
actionModeCallback需要另行构造
在onCreateActionMode中填充菜单
onActionModeItemClicked中写点击回调
ListView本身在layout中只是占位用,实际内容需另外构建item.xml文件。
比较简单的使用,数据类型比较单一的时候,listview占位:
item.xml,只有一个TextView
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
android:padding="5dp"
/>
activity中;
使用ArrayAdapter填充item.xml,给listview设置adapter,adapter添加数据。
//初使化设备适配器存储数组
mPairedDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);
mUnPairedDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);
//设置已配队设备列表
ListView pairedListView = findViewById(R.id.pairedListView);
pairedListView.setAdapter(mPairedDevicesArrayAdapter);
pairedListView.setOnItemClickListener( mDeviceClickListener);
// 设置新查找设备列表
ListView newDevicesListView = findViewById(R.id.unPairedListView);
newDevicesListView.setAdapter(mUnPairedDevicesArrayAdapter);
newDevicesListView.setOnItemClickListener(mDeviceClickListener);
// 得到本地蓝牙句柄
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
// //添加已配对设备到列表并显示
if (pairedDevices.size() > 0) {
findViewById(R.id.pairedListView).setVisibility(View.VISIBLE);
for (BluetoothDevice device : pairedDevices) {
mPairedDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
}
} else {
String noDevices = "没有找到已配对的设备。" ;
mPairedDevicesArrayAdapter.add(noDevices);
}
较为复杂的实现,adapter需要自己定义。
例如聊天界面的消息列表
listview占位不赘述。
itme的xml形式:
主要包括气泡框,发送时间,头像,发送内容这些数据。
<LinearLayout style="@style/jmui_chat_item_send_layout"
xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
android:id="@+id/tv_sendtime"
style="@style/jmui_chat_text_date_style"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:gravity="right"
android:orientation="horizontal">
<ImageButton
android:id="@+id/jmui_fail_resend_ib"
android:layout_width="21dp"
android:layout_height="21dp"
android:layout_gravity="center_vertical"
android:layout_marginRight="5dp"
android:background="@drawable/send_message_failed"
android:clickable="true"
android:scaleType="fitCenter"
android:visibility="gone"/>
<LinearLayout
style="@style/jmui_msg_content_style"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="3dp"
android:autoLink="web"
android:background="@drawable/jmui_msg_send_bg"
android:paddingLeft="10dp"
android:paddingRight="18dp"
android:textColor="#363333"
android:textColorLink="#157BFB"
android:orientation="vertical">
<TextView
style="@style/jmui_msg_text_style"
android:id="@+id/tv_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColorLink="#157BFB"
android:visibility="visible"/>
<ImageView
style="@style/jmui_msg_img_style"
android:id="@+id/iv_content"
android:adjustViewBounds="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"/>
LinearLayout>
<LinearLayout
android:id="@+id/ll_push"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="3dp"
android:background="@drawable/business_card_bg"
android:orientation="vertical"
android:visibility="gone">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/iv_push_img"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginBottom="12.33dp"
android:layout_marginLeft="11dp"
android:layout_marginTop="13.33dp"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="14dp"
android:orientation="vertical">
<TextView
android:id="@+id/tv_menuName"
android:layout_width="120dp"
android:layout_height="wrap_content"
android:layout_marginLeft="11.33dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="#2C2C2C"
android:textSize="15sp"/>
<TextView
android:id="@+id/tv_pushContent"
android:layout_width="120dp"
android:layout_height="wrap_content"
android:layout_marginLeft="11.33dp"
android:layout_marginTop="4dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="#999999"
android:textSize="12sp"/>
LinearLayout>
LinearLayout>
<View
android:layout_width="183dp"
android:layout_height="0.33dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="16.67dp"
android:background="#DCDCDC"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="3dp"
android:layout_marginLeft="10dp"
android:layout_marginTop="3dp"
android:text="@string/Message_push"
android:textColor="#989898"
android:textSize="10sp"/>
LinearLayout>
<ImageView
android:id="@+id/jmui_avatar_iv"
style="@style/jmui_chat_item_sender_avatar_style"
android:layout_width="@dimen/avatar_list_size"
android:layout_height="@dimen/avatar_list_size"
android:scaleType="fitCenter"
android:gravity="center_vertical" />
LinearLayout>
LinearLayout>
package com.beidouapp.model.adapters;
import static androidx.core.content.ContextCompat.startActivity;
import static com.beidouapp.model.utils.ImageUtil.getDefaultBitmap;
import static com.beidouapp.model.utils.ImageUtil.isImage;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.media.MediaMetadataRetriever;
import android.media.MediaPlayer;
import android.net.Uri;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.activity.result.contract.ActivityResultContracts;
import com.alibaba.fastjson.JSON;
import com.beidouapp.Config;
import com.beidouapp.R;
import com.beidouapp.model.messages.ChatMessage;
import com.beidouapp.model.messages.Position;
import com.beidouapp.model.utils.ImageUtil;
import com.beidouapp.ui.ZoomPhoto;
import com.beidouapp.ui.ZoomPosition;
import com.beidouapp.ui.ZoomVideo;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
public class ChatAdapter extends BaseAdapter {
List<ChatMessage> chatMessageList;
LayoutInflater inflater;
Context context;
public ChatAdapter(Context context, List<ChatMessage> list) {
this.chatMessageList = list;
this.context = context;
inflater = LayoutInflater.from(context);
}
@Override
public int getViewTypeCount() {
return 2;
}
@Override
public int getItemViewType(int position) {
if (chatMessageList.get(position).getIsMeSend() == 1)
return 0;
else
return 1;
}
@Override
public int getCount() {
return chatMessageList.size();
}
@Override
public Object getItem(int position) {
return chatMessageList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
String fileName;
ChatMessage chatMessage = chatMessageList.get(position);
String content = chatMessage.getContent();
// int start=content.lastIndexOf("/");
// int end=content.lastIndexOf(".");
// if(start!=-1 && end!=-1){
// fileName = content.substring(start+1,end);
// }else{
// fileName = null;
// }
// Log.d("zw", "getView: " + content + " " + fileName + ".amr");
String extraContent = "File//sdcard/BeidouAPP/Download/" + "2d46ee8c-69ab-445f-b960-7ec3b56672e3" + ".amr";
Log.d("zw", "getView: " + extraContent);
String time = formatTime(chatMessage.getTime());
String type = chatMessage.getType();
String nickname = chatMessage.getName();
int isMeSend = chatMessage.getIsMeSend();
String avatar = chatMessage.getAvatar();
String id = chatMessage.getId();
//int isRead = chatMessage.getIsRead();
final ViewHolder holder;
if (convertView == null) {
holder = new ViewHolder();
if (isMeSend == 0) {
convertView = inflater.inflate(R.layout.item_chat_receive_text, parent, false);
holder.tv_content = convertView.findViewById(R.id.tv_content);
holder.tv_sendtime = convertView.findViewById(R.id.tv_sendtime);
holder.iv_content = convertView.findViewById(R.id.iv_content);
holder.tv_display_name = convertView.findViewById(R.id.tv_display_name);
holder.avatar = convertView.findViewById(R.id.tv_display_avatar);
} else {
convertView = inflater.inflate(R.layout.item_chat_send_text, parent, false);
holder.tv_content = convertView.findViewById(R.id.tv_content);
holder.iv_content = convertView.findViewById(R.id.iv_content);
holder.tv_sendtime = convertView.findViewById(R.id.tv_sendtime);
holder.avatar = convertView.findViewById(R.id.jmui_avatar_iv);
}
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
if (avatar.equals(holder.avatar.getTag())){}
else {
ImageUtil.loadAvatar(context, id, avatar, holder.avatar);
holder.avatar.setTag(avatar);
}
holder.tv_sendtime.setText(time);
if (type.equals("text")) {
holder.tv_content.setVisibility(View.VISIBLE);
holder.tv_content.setText(content);
holder.iv_content.setVisibility(View.GONE);
}
else if (type.equals("img")) {
try {
holder.tv_content.setVisibility(View.GONE);
Bitmap bitmap = BitmapFactory.decodeFile(content);
holder.iv_content.setImageBitmap(bitmap);
holder.iv_content.setVisibility(View.VISIBLE);
holder.iv_content.setOnClickListener(view -> {
Intent intent = new Intent(context, ZoomPhoto.class);
intent.putExtra("path",content);
context.startActivity(intent);
});
} catch (Exception e) {
holder.tv_content.setVisibility(View.VISIBLE);
holder.tv_content.setText("[图片已损坏]");
holder.iv_content.setVisibility(View.GONE);
}
}
else if (type.equals("audio")) {
holder.tv_content.setVisibility(View.GONE);
holder.iv_content.setImageResource(R.drawable.icon_voice_press);
holder.iv_content.setVisibility(View.VISIBLE);
holder.iv_content.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
try {
MediaPlayer mediaPlayer = MediaPlayer.create(context, Uri.parse(content));
mediaPlayer.start();
// MediaPlayer mediaPlayer = MediaPlayer.create(context, Uri.parse(extraContent));
// mediaPlayer.start();
}catch (Exception e){
Log.d("zw", "onClick: 此时使用第二个方法加载");
try {
MediaPlayer mediaPlayer = MediaPlayer.create(context, Uri.parse(extraContent));
mediaPlayer.start();
}catch (Exception exception){
Log.d("zw", "onClick: 第二个方法也错了");
exception.printStackTrace();
}
}
}
});
}
else if (type.equals("video")) {
try {
holder.tv_content.setVisibility(View.GONE);
MediaMetadataRetriever media = new MediaMetadataRetriever();
media.setDataSource(content);
Bitmap baseBitmap = media.getFrameAtTime();
holder.iv_content.setImageBitmap(ImageUtil.getVideoImg(context, baseBitmap));
holder.iv_content.setVisibility(View.VISIBLE);
holder.iv_content.setOnClickListener(view -> {
Intent intent = new Intent(context, ZoomVideo.class);
intent.putExtra("path",content);
Log.d("视频", "getView: "+content);
context.startActivity(intent);
});
} catch (Exception e) {
holder.tv_content.setVisibility(View.VISIBLE);
holder.tv_content.setText("[视频已损坏]");
holder.iv_content.setVisibility(View.GONE);
}
}
else if (type.equals("notice")) {
holder.tv_content.setVisibility(View.VISIBLE);
holder.tv_content.setText("群公告:"+ content);
holder.iv_content.setVisibility(View.GONE);
}
else if (type.equals("pos")) {
holder.tv_content.setVisibility(View.VISIBLE);
holder.iv_content.setVisibility(View.VISIBLE);
Position pos = JSON.parseObject(content, Position.class);
holder.tv_content.setText("位置:" + pos.getText());
holder.iv_content.setImageResource(R.drawable.pos_img);
holder.iv_content.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(context, ZoomPosition.class);
intent.putExtra("pos", pos);
context.startActivity(intent);
}
});
}
if (isMeSend == 1) {
}else{
holder.tv_display_name.setVisibility(View.VISIBLE);
holder.tv_display_name.setText(nickname);
}
return convertView;
}
class ViewHolder {
private TextView tv_content;
private TextView tv_sendtime;
private TextView tv_display_name;
private ImageView avatar;
private ImageView iv_content;
//private TextView tv_isRead;
}
private String formatTime(String timeMillis) {
long timeMills=Long.parseLong(timeMillis);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = new Date(timeMills);
return simpleDateFormat.format(date);
}
}
这个adapter的编写思路:
1- public class ChatAdapter extends BaseAdapter
然后实现方法:
四个方法分别是获取列表长度,根据位置索引获取到某个item,根据位置索引获取itme的id,获取视图
2-构建子类viewHolder用于声明item中的各个布局组件
3- alt+inser,选择‘construct’,构造方法,开放接口给activity,以便输出context和数据列表:
这里还传输了inflater,是为了渲染item.xml
4-在getview中,使用inflater渲染item.xml,并实例化viewHolder中的各个布局组件,进行数据设置。
5-在activity中使用自定义adapter:
/**
* 更新消息列表
*/
private void initChatMsgListView() {
adapter = new ChatAdapter(context, chatMessageList);
listView.setAdapter(adapter);
listView.setSelection(chatMessageList.size());
listView.setTranscriptMode(ListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);
}
这里 listView.setSelection(chatMessageList.size());将列表选中最后一个item,
listView.setTranscriptMode(ListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);
如果是 TRANSCRIPT_MODE_ALWAYS_SCROLL:则 强制 从 ListView 的底部开始刷新;
如果是 TRANSCRIPT_MODE_NORMAL:如果当前的最后一个 Item 在 ListView 显示范围内,adapter 数据集内容变化时就从滚动底部;否则不滚动到底部!