Android 考试复习

判断题(20分)
单项选择题(30分)
编程题(40分)
论述题(10分)

需要权限的操作
  1. 网络权限

    在 AndroidManifest.xml 添加
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    
  2. 外存储公共空间

    manifest 中声明外存储写入权限
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    

    注意:
    Internal Storage (内存储): 无需声明权限,其他应用无法访问的应用程序的独立空间。文件随应用删除而删除,空间有限,放应用必须文件

    External Storage:

    • 外存储私有空间: 无需声明权限,随应用卸载删除,放普通文件、缓存文件。挂载到:/mnt/sdcard/android/data/packname/files
    • 外存储公共空间: 需声明权限,Android公共目录、音乐、图片等等。挂载到:挂载到:/mnt/sdcard/

启动activity及传参的方法
  • Intent intent = new Intent(this, SecondActivity.class)
  • intent.putExtra("value", "dafadfadfa")
  • startActivity(intent)
  • String value = getIntent().getStringExtra("value")
//MainActivity中

Intent intent = new Intent(this, SecondActivity.class);  //(上下文环境, 目的Activity)
intent.putExtra("value", "dafadfadfa");   //在第一个页面以 键值对 将数据装进Intent   
startActivity(intent);   //startActivity方法

//--------------------------------------------------------------

//SecondActivity中
...
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_sec);
    String value = getIntent().getStringExtra("value");  //在第二个页onCreate()面拿到数据 
}
...
Activity的主要生命周期回调函数

Activity 生命周期回调方法汇总表

方法说明是否能事后终止?后接
onCreate()首次创建 Activity 时调用。 您应该在此方法中执行所有正常的静态设置 — 创建视图、将数据绑定到列表等等。 系统向此方法传递一个 Bundle 对象,其中包含 Activity 的上一状态,不过前提是捕获了该状态。始终后接 onStart()onStart()
onRestart()在 Activity 已停止并即将再次启动前调用。始终后接 onStart()onStart()
onStart()在 Activity 即将对用户可见之前调用。如果 Activity 转入前台,则后接 onResume(),如果 Activity 转入隐藏状态,则后接 onStop()onResume()onStop()
onResume()在 Activity 即将开始与用户进行交互之前调用。 此时,Activity 处于 Activity 堆栈的顶层,并具有用户输入焦点。始终后接 onPause()onPause()
onPause()当系统即将开始继续另一个 Activity 时调用。 此方法通常用于确认对持久性数据的未保存更改、停止动画以及其他可能消耗 CPU 的内容,诸如此类。 它应该非常迅速地执行所需操作,因为它返回后,下一个 Activity 才能继续执行。如果 Activity 返回前台,则后接 onResume(),如果 Activity 转入对用户不可见状态,则后接 onStop()onResume()onStop()
onStop()在 Activity 对用户不再可见时调用。如果 Activity 被销毁,或另一个 Activity(一个现有 Activity 或新 Activity)继续执行并将其覆盖,就可能发生这种情况。如果 Activity 恢复与用户的交互,则后接 onRestart(),如果 Activity 被销毁,则后接 onDestroy()onRestart()onDestroy()
onDestroy()在 Activity 被销毁前调用。这是 Activity 将收到的最后调用。 当 Activity 结束(有人对 Activity 调用了 finish()),或系统为节省空间而暂时销毁该 Activity 实例时,可能会调用它。 您可以通过 isFinishing() 方法区分这两种情形。
LinearLayout的基本布局方法;基本控件属性,高宽,比重等
  • layout_width,layout_height 宽高:match_parent(匹配父容器)wrap_content(自适应)
  • android:orientation:LinearLayout方向 : vertical , horizontal
  • android:layout_gravity :相对于该控件的父组件,控件本身的显示位置。 仅在LinearLayout内有效,受android:orientation属性影响 bottom , center
  • android:gravity:控件内内容的显示位置
  • android:layout_weight:比重,android:orientation 相应方向的值需设为0dp android:layout_width="0dp" android:layout_weight="1"
在Activity中获取布局控件的方法

findViewById():基于ID名称获取组件

public class MainActivity extends AppCompatActivity {
    private EditText editTextUserName;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        editTextUserName = findViewById(R.id.act_main_editText_username);
        ...
常用事件回调接口

2种实现监听的方法

  • 匿名内部类
  • lambda表达式 (set language level to 8)

View.OnClickListener: onClick() : 点击监听回调函数

//onCreate()中
buttonSubmit = findViewById(R.id.act_main_button_submit);
//匿名内部类
buttonSubmit.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        String string = editTextUserName.getText().toString();
        textViewUserName.setText(string);
    }
});

//lambda表达式
buttonSubmit.setOnClickListener(v -> {
    String string = editTextUserName.getText().toString();
    textViewUserName.setText(string);
});

EditText: TextChangedListener : 文本变化监听函数

//onCreate()中
editTextNameChange = findViewById(R.id.act_main_editText_change);
//匿名内部类
editTextNameChange.addTextChangedListener(new TextWatcher() {
    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
		//变化前
    }

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
        //变化时
        textViewNamechange.setText(s);
    }

    @Override
    public void afterTextChanged(Editable s) {
		变化后
    }
});

View.OnFocusChangeListener: onFocusChange()
View.OnTouchListener: onTouch()
View.OnKeyListener: onKey()

基本控件及属性

???

理解MVVM设计模式,以及在Android中的应用

View
View层做的就是和UI相关的工作,我们只在XML和Activity或Fragment写View层的代码,View层不做和业务相关的事,也就是我们的Activity 不写和业务逻辑相关代码,也不写需要根据业务逻辑来更新UI的代码,因为更新UI通过Binding实现,更新UI在ViewModel里面做(更新绑定的数据源即可) 简单的说:View层不做任何业务逻辑、不涉及操作数据、不处理数据、UI和数据严格的分开

ViewModel
ViewModel层做的事情刚好和View层相反,ViewModel 只做和业务逻辑和业务数据相关的事,不做任何和UI、控件相关的事,ViewModel 层不会持有任何控件的引用,更不会在ViewModel中通过UI控件的引用去做更新UI的事情。

Model
Model 的职责很简单,基本就是实体模型(Bean)同时包括Retrofit 的Service

基于键值对保存数据的接口及方法 (SharedPreferences 接口)

link

Saving Key-Value Sets:

自定义application,暴露获取application对象的静态方法

public class MyApplication extends Application {
    private static MyApplication instance;
    public static MyApplication getInstance() {
        return instance;
    }
    @Override
    public void onCreate() {
        super.onCreate();
        instance = this;
    }
}

修改 AndroidManifest 配置启动自定义application
android:name=".util.MyApplication"

创建 SharedPreferences 操作工具类

/**
 * 有则直接使用,没有则创建名为为pre_my的XML配置文件
 * 位置data\data\packagename\shared_prefs
 * 声明使用范围
 */
public class SharedPreferencesUtils {
    private static SharedPreferences sf = create();
    private static final String PRE_FILE = "pre_my";
    private static final String MYEDIT = "myedit";

    /**
     * 封装SharedPreferences的构建过程,对外仅暴露允许修改的方法
     * @return
     */
    private static SharedPreferences create() {
        return MyApplication.getInstance()
                .getSharedPreferences(PRE_FILE, Context.MODE_PRIVATE);
    }

    public static void putMyedit (String myedit) {
        sf.edit().putString(MYEDIT, myedit).apply();
    }

    public static String getMyedit() {
        return sf.getString(MYEDIT, "");
    }

}

在activity中将输入写入文件

// 在Main_Activity.java 中
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    editText = findViewById(R.id.act_main_edittext);
    textView = findViewById(R.id.act_main_textView);
    button = findViewById(R.id.act_main_button);
    // 获取值,没有默认值为空
    textView.setText(SharedPreferencesUtils.getMyedit());

    button.setOnClickListener(v -> {
        SharedPreferencesUtils.putMyedit(editText.getEditableText().toString());
    });
}
了解ViewModel中声明可观测数据,并在布局绑定的方法

link

在项目gradle配置中,启动dataBinding

dataBinding {
        enabled = true
}

添加整合了viewmodel livedata 的依赖lifecycle-extensions

def lifecycle_version = "2.0.0"
// ViewModel and LiveData
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
  • 官方推荐一个activity对应绑定一个ViewModel

创建实体类

创建自定义viewmodel类

声明 页面数据绑定/生命周期绑定的MutableLiveData类型数据

创建修改方法,在子线程中修改数据

  • setValue() 在主线程 中更新数据
  • postValue() 在子线程 中更新数据 : 自动通知主线程修改
  • getValue()
public class MainViewModel extends AndroidViewModel {
    private static final String TAG = "MainViewModel";
    // 将预在页面绑定的数据,声明为与页面生命周期绑定的MutableLiveData可变类型
    // 可以想象为一个能够绑定到视图页面的容器
    public MutableLiveData<User> userLiveData = new MutableLiveData<>();

    /**
     * 必须声明的构造函数
     * 自动注入application对象,便于后期使用
     * @param application
     */
    public MainViewModel(@NonNull Application application) {
        super(application);
        User user = new User("BO");
        userLiveData.setValue(user); //在主线程 中更新数据 
    }

    /**
     * 在子线程中异步操作修改绑定数据,而非主线程
     * 因此必须使用postValue()方法,自动通知主线程修改
     */
    public void change() {
        new Thread(() -> {
            try {
                Thread.sleep(2000);
                User u = userLiveData.getValue();
                u.name = "SUN";
                // 也可重新重新创建一个user对象,置入
                userLiveData.postValue(u);
            } catch (InterruptedException e) {
            }
        }).start();
    }
}

修改layout文件,添加数据绑定标签<data><variable/></data>

绑定自定义的ViewModel类

在控件,通过表达式绑定数据,或方法

双向绑定 @={}

对象中的属性改变时,更新不会通知

<?xml version="1.0" encoding="utf-8"?>
<layout>
    <data>
        <variable
            name="mianVM"
            type="com.example.example12.viewmodel.MainViewModel" />
    </data>
    <LinearLayout 
        ...
        tools:context=".MainActivity">

        <TextView
            ...
            android:text="@{mianVM.userLiveData.name}" />
        
        <!-- VM方法不能耦合view对象:vm 不能绑定组件,应为可能已经被销毁了 -->
        <Button
            android:onClick="@{() -> mianVM.change()}"
            android:text="异步改变值" />
        
        <!-- 直接调用activity中的方法 -->
        <Button
            ...
            android:onClick="onButtonClick"
            android:text="To SecActivity" />

    </LinearLayout>
</layout>

修改activity代码,获取自定义动态创建的binding对象,获取自定义viewmodel对象

将vm绑定到UI页面

将绑定数据绑定到activity生命周期

  • Activity 的onCreate() 中基于layout文件生成绑定对象 (setContentView 删去)

  • 绑定类会基于 layout 中生成的变量,自动生成 getter/setter 方法

  • 绑定自定义的 ViewModel 类 :binding.setMianVM(mainViewModel);

  • 将绑定数据,与当前activity生命周期绑定:binding.setMianVM(mainViewModel); 如,当数据改变时,且activity可见时,自动更新页面

  • activity有处理UI,跳转更新等操作,业务逻辑操作由vm负责

    vm 不能绑定组件,应为可能已经被销毁了

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 取消默认调用setContentView()方法代码:设置该activity 要渲染的布局UI

        /**
         * 必须先在layout中使用data标签声明变量
         * 才能基于layout文件的命名,动态自动生成ActivityMainBinding类
         * The Data Binding Library generates binding classes that are used to access the layout's variables and views. 
         */
        ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        // 必须通过工具类获取VM对象,不能手动创建
        MainViewModel mainViewModel = ViewModelProviders.of(this).get(MainViewModel.class);
        // 基于layout中声明的变量,自动生成属性变量的getter/setter方法
        binding.setMianVM(mainViewModel);
        /**
         * 将绑定数据,与当前activity生命周期绑定
         * 例如,当数据改变时,且activity可见时,自动更新页面
         */
        binding.setLifecycleOwner(this);
    }
    /**
     * activity有处理UI,跳转更新等操作,业务逻辑操作由vm负责
     * @param view
     */
    public void onButtonClick(View view) {
        Log.i(TAG, "onButtonClick: ");
        Intent intent = new Intent(this, SecActivity.class);
        startActivity(intent);
    }
}
了解侧滑的实现方法

ItemTouchHelper使用步骤

自定义callback继承 ItemTouchHelper.Callback -> 在内部写一个数据操作接口 -> 重写触发方法调用接口 -> 适配器实现数据操作接口,重新方法 -> acctivity 中附着 recyclerview

  1. 创建 MyCallback 继承 ItemTouchHelper.Callback

  2. 把数据操作部分抽象成一个接口 AdapterCallback,这个接口可以写在 MyCallback

    因为ItemTouchHelper在完成触摸的各种动作后,就要对Adapter的数据进行操作,比如侧滑删除操作

  3. 自定义 adapter 实现接口MyCallback.AdapterCallback, 重写相关方法

  4. MainActivity

    ItemTouchHelper helper = new ItemTouchHelper(new MyCallback(adapter));
    helper.attachToRecyclerView(recyclerView);
    

具体实现:

MyCallback 及内部接口 AdapterCallback

//MyCallback 及内部接口 AdapterCallback
public class MyCallback extends ItemTouchHelper.Callback {
    private static final String TAG = "MyCallback";
    private AdapterCallback adapterCallback;

    public interface AdapterCallback { //数据操作接口
        void remove(int position);
    }

    public MyCallback(AdapterCallback adapterCallback) {
        this.adapterCallback = adapterCallback;
    }

    /**
     * 该方法返回一个整数,用来指定拖拽和滑动在哪个方向是被允许的。
     * 在其中使用makeMovementFlags(int dragFlags, int swipeFlags)返回,
     * 该方法第一个参数用来指定拖动,第二个参数用来指定滑动。
     * ItemTouchHelper.UP  //滑动拖拽向上方向
     * ItemTouchHelper.DOWN//向下
     * ItemTouchHelper.LEFT//向左
     * ItemTouchHelper.RIGHT//向右
     * ItemTouchHelper.START//依赖布局方向的水平开始方向
     * ItemTouchHelper.END//依赖布局方向的水平结束方向
     *
     * @param recyclerView
     * @param viewHolder
     * @return
     */
    @Override
    public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
        // Log.i(TAG, "" + "getMovementFlags");
        //允许上下的拖动
        int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
        //只允许从右向左侧滑
        int swipeFlags = ItemTouchHelper.LEFT;
        return makeMovementFlags(0, swipeFlags);
    }

    /**
     * onMove方法是拖拽的回调,参数viewHolder是拖动的Item,target是拖动的目标位置的Item,
     * 该方法如果返回true表示切换了位置,反之返回false。
     *
     * @param recyclerView
     * @param viewHolder
     * @param target
     * @return
     */
    @Override
    public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {

        return false;
    }

    /**
     * onSwiped方法为Item滑动回调,viewHolder为滑动的item,direction为滑动的方向。
     *
     * @param viewHolder
     * @param direction
     */
    @Override
    public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
        switch (direction) {
            case ItemTouchHelper.LEFT:
                adapterCallback.remove(viewHolder.getAdapterPosition());
        }

    }
}

自定义适配器并实现操作接口

//自定义适配器并实现操作接口
public class MainAdapter extends RecyclerView.Adapter<MainAdapter.MyViewHolder> implements MyCallback.AdapterCallback {
    private static final String TAG = "MainAdapter";
    private List<News> news;
    public MainAdapter(List<News> news) {
        this.news = news;
    }

    @NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {

        View itemView = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.recyclerview_news, parent, false);
        return new MyViewHolder(itemView);
    }

    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
        holder.title.setText(news.get(position).title);
        holder.suttitle.setText(news.get(position).subtitle);
        holder.pic.setImageResource(R.mipmap.ic_launcher);
    }

    @Override
    public int getItemCount() {
        return news.size();
    }

    @Override
    public void remove(int position) {
        news.remove(position);
        // 指定通知可提高渲染效率,同时支持动画
        notifyItemRemoved(position);
    }

    static class MyViewHolder extends RecyclerView.ViewHolder {
        TextView title;
        TextView suttitle;
        ImageView pic;

        public MyViewHolder(@NonNull View itemView) {
            super(itemView);
            title = itemView.findViewById(R.id.news_title);
            suttitle = itemView.findViewById(R.id.news_subtitle);
            pic = itemView.findViewById(R.id.news_pic);
        }
    }
}

MainActivity配置

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    private RecyclerView recyclerView;
    private MainAdapter adapter;
    private SwipeRefreshLayout swipe;
    private List<News> news = listNews();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        recyclerView = findViewById(R.id.act_main_recyclerview);
        // 指定一个默认的布局管理器
        RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(this);
        recyclerView.setLayoutManager(mLayoutManager);
        // 指定item插入/移除动画
        DefaultItemAnimator animator = new DefaultItemAnimator();
        // 包含默认的操作动画事件,也可自定义动画时间
        animator.setRemoveDuration(500);
        animator.setMoveDuration(500);
        recyclerView.setItemAnimator(animator);
        // 指定item分割线
        recyclerView.addItemDecoration(new DividerItemDecoration(this, LinearLayoutManager.VERTICAL));
        // 指定适配器
        adapter = new MainAdapter(news);
        recyclerView.setAdapter(adapter);

        ItemTouchHelper helper = new ItemTouchHelper(new MyCallback(adapter));
        helper.attachToRecyclerView(recyclerView);

        swipe = findViewById(R.id.act_third_swipe);
        swipe.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                //模拟耗时操作,子线程禁止直接修改主线程控件属性
                new Handler().postDelayed(() -> {
                    //取消刷新动画
                    swipe.setRefreshing(false);
                    news.add(0, new News(1, "阿根廷VS波黑" + news.size(), "小组赛F组 阿根廷VS波黑"));
                    // 指定通知可提高渲染效率,同时支持动画
                    adapter.notifyItemInserted(0);
                    recyclerView.scrollToPosition(0);
                }, 500);
            }
        });
    }

    private List<News> listNews() {
        List<News> news = new ArrayList<>();
        News n1 = new News(1, "阿根廷VS波黑", "小组赛F组 阿根廷VS波黑");
        News n2 = new News(2, "法国VS洪都拉斯", "小组赛E组 法国VS洪都拉斯");
        News n3 = new News(3, "瑞士VS厄瓜多尔", "小组赛E组 瑞士VS厄瓜多尔");
        news.add(n1); news.add(n2); news.add(n3);
        return news;
    }
}

创建menu资源目录; 创建menu布局文件; 设置item title,icon等属性: link

  • app:showAsAction属性: always, collapseActionView, ifRoom, never, withText

重写activity onCreateOptionsMenu()方法,加载menu布局 link

    ...
    /**
     * 重写,加载menu布局
     * @param menu
     * @return
     */
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu, menu);
        return true;
    }

重写activity onOptionsItemSelected()方法,监听item点击事件 link

    ...
    /**
     * 重写,监听menu点击事件
     * @param item
     * @return
     */
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        String msg = "";
        switch (item.getItemId()) {
            case R.id.menu_add:
                msg = "add";
                break;
            // 点击左箭头,返回,即关闭当前activity
            case android.R.id.home:
                msg = "home";
                finish();
            case R.id.menu_send:
                msg = "send";
                break;
            case R.id.menu_edit:
                msg = "edit";
                break;
            case R.id.menu_del:
                msg = "delete";
                break;
        }
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
        return super.onOptionsItemSelected(item);
    }
    ...
了解通知的实现方法

link

构造Notification,声明必须属性

NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "default");
// 通知必须包含:图标,标题,文本内容
builder.setSmallIcon(R.mipmap.ic_launcher);
builder.setContentTitle(editText.getText().toString());
builder.setContentText(editText2.getText().toString());
// 设置提醒方法,震动、铃声等
builder.setDefaults(Notification.DEFAULT_ALL);
// 读取后是否自动在通知栏删除
builder.setAutoCancel(true);

创建PendingIntent对象,封装点击通知intent

// 当点击通知时的执行
Intent intent = new Intent(this, ResultActivity.class);
intent.putExtra("title", editText.getText().toString());
intent.putExtra("text", editText2.getText().toString());
// 预处理intent,即延迟执行的intent
PendingIntent pIntent = PendingIntent.getActivity(this,
        0,
        intent,
        PendingIntent.FLAG_UPDATE_CURRENT);  // 更新当前通知内容,没有通知则创建
builder.setContentIntent(pIntent);
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

基于版本构造NotificationChannel

//8.0 以后需要加上channelId 才能正常显示
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    String channelId = "default";
    String channelName = "默认通知";
    manager.createNotificationChannel(new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_HIGH));
}

发送通知

// 当发送ID相同的通知时,基于Pending设置,决定通知显示方法
manager.notify(count, builder.build());
count++;
编程
点击按钮,将输入框中的值,赋值给输出控件
private EditText editTextUserName;
private Button buttonSubmit;
private TextView textViewUserName;

------------------------------------
//onCreate()中
editTextNameChange  =findViewById(R.id.act_main_editText_change);
textViewUserName = findViewById(R.id.act_main_textView_username);
buttonSubmit = findViewById(R.id.act_main_button_submit);
//匿名内部类
buttonSubmit.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        String string = editTextUserName.getText().toString();
        textViewUserName.setText(string);
    }
});
构造一个带点击实现的单项对话框

单项???????

// 单选按钮组
AlertDialog.Builder dialog3 = new AlertDialog.Builder(this);
dialog3.setTitle("水果");
dialog3.setItems(arrayFruit, new DialogInterface.OnClickListener() {
    // 传入的which为被选中的位置
    @Override
    public void onClick(DialogInterface dialog, int which) {
        Toast.makeText(MainActivity.this, arrayFruit[which], Toast.LENGTH_SHORT).show();
    }
});
dialog3.show();

如果是单选就不看这个了

AlertDialog.Builder dialog2 = new AlertDialog.Builder(this);
dialog2.setTitle("删除");
dialog2.setMessage("确定删除吗?");
// 确认按钮回调
dialog2.setPositiveButton("确定", new DialogInterface.OnClickListener() {
	@Override
    public void onClick(DialogInterface dialog, int which) {
    }
});
// 取消回调
dialog2.setNegativeButton("取消", new DialogInterface.OnClickListener() {
	@Override
	public void onClick(DialogInterface dialog, int which) {
    	dialog.dismiss();
    }
});
// 中间回调
dialog2.setNeutralButton("详细内容", new DialogInterface.OnClickListener() {
	@Override
	public void onClick(DialogInterface dialog, int which) {
    }
});
dialog2.show();
创建一个RecyclerView.Adapter,自定义ViewHolder,实现基于给定数据与布局信息,渲染RecyclerView
public class MainAdapter extends RecyclerView.Adapter<MainAdapter.MyViewHolder> {
    private static final String TAG = "MainAdapter";
    private List<News> news;

    public MainAdapter(List<News> news) {
        this.news = news;
    }

    /**
     * 基于指定样式,渲染item,并将item对象绑定到viewholder
     * 当回收缓存区没有VH时,回调
     * 基于布局创建item对象,创建VH封装item对象,返回VH以复用
     * 因此,仅会回调有限次数。不同系统版本,不同屏幕尺寸等等均不同
     *
     * @param parent
     * @param viewType
     * @return
     */
    @NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        Log.i(TAG, "onCreateViewHolder");
        // 每一个item对象
		//将一个xml布局文件转换成一个View
        View itemView = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.recyclerview_news, parent, false);
        // 由VM持有
        return new MyViewHolder(itemView);
    }

    /**
     * 当渲染指定位置item时,从回收缓存区注入一个相同布局类型的VH,以及item的位置
     * 从vm中获取持有的控件对象,将指定集合位置的数据加载到控件,渲染
     *
     * @param holder
     * @param position
     */
    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
        Log.i(TAG, "onBindViewHolder: " + position + "/" + news.get(position).title);
        holder.title.setText(news.get(position).title);
        holder.suttitle.setText(news.get(position).subtitle);
        // 模拟图片
        holder.pic.setImageResource(R.mipmap.ic_launcher);
    }

    // 必须指定初始化时item数量,后期可动态改变
    @Override
    public int getItemCount() {
        return news.size();
    }

    /**
     * VH的作用是保持item view上的控件对象的引用
     * 避免在复用item上反复findViewById
     */
    static class MyViewHolder extends RecyclerView.ViewHolder {
        TextView title;
        TextView suttitle;
        ImageView pic;

        public MyViewHolder(@NonNull View itemView) {
            super(itemView);
            title = itemView.findViewById(R.id.news_title);
            suttitle = itemView.findViewById(R.id.news_subtitle);
            pic = itemView.findViewById(R.id.news_pic);
        }
    }
}