判断题(20分)
单项选择题(30分)
编程题(40分)
论述题(10分)
需要权限的操作
网络权限
1 2 3
| 在 AndroidManifest.xml 添加 <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
外存储公共空间
1 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")
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
Intent intent = new Intent(this, SecondActivity.class); //(上下文环境, 目的Activity) intent.putExtra("value", "dafadfadfa"); startActivity(intent);
... @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_sec); String value = getIntent().getStringExtra("value"); } ...
|
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名称获取组件
1 2 3 4 5 6 7 8 9
| 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() : 点击监听回调函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 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); } });
buttonSubmit.setOnClickListener(v -> { String string = editTextUserName.getText().toString(); textViewUserName.setText(string); });
|
EditText: TextChangedListener : 文本变化监听函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| 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对象的静态方法
1 2 3 4 5 6 7 8 9 10 11
| 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 操作工具类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
|
public class SharedPreferencesUtils { private static SharedPreferences sf = create(); private static final String PRE_FILE = "pre_my"; private static final String MYEDIT = "myedit";
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中将输入写入文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @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
1 2 3
| dataBinding { enabled = true }
|
添加整合了viewmodel livedata 的依赖lifecycle-extensions
1 2 3
| def lifecycle_version = "2.0.0" // ViewModel and LiveData implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
|
- 官方推荐一个activity对应绑定一个ViewModel
创建实体类
创建自定义viewmodel类
声明 页面数据绑定/生命周期绑定的MutableLiveData类型数据
创建修改方法,在子线程中修改数据
setValue() 在主线程 中更新数据
postValue() 在子线程 中更新数据 : 自动通知主线程修改
getValue()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| public class MainViewModel extends AndroidViewModel { private static final String TAG = "MainViewModel"; public MutableLiveData<User> userLiveData = new MutableLiveData<>();
public MainViewModel(@NonNull Application application) { super(application); User user = new User("BO"); userLiveData.setValue(user); }
public void change() { new Thread(() -> { try { Thread.sleep(2000); User u = userLiveData.getValue(); u.name = "SUN"; userLiveData.postValue(u); } catch (InterruptedException e) { } }).start(); } }
|
修改layout文件,添加数据绑定标签<data><variable/></data>
绑定自定义的ViewModel类
在控件,通过表达式绑定数据,或方法
双向绑定 @={}
对象中的属性改变时,更新不会通知
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| <?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}" /> <Button android:onClick="@{() -> mianVM.change()}" android:text="异步改变值" /> <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 不能绑定组件,应为可能已经被销毁了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity";
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main); MainViewModel mainViewModel = ViewModelProviders.of(this).get(MainViewModel.class); binding.setMianVM(mainViewModel);
binding.setLifecycleOwner(this); }
public void onButtonClick(View view) { Log.i(TAG, "onButtonClick: "); Intent intent = new Intent(this, SecActivity.class); startActivity(intent); } }
|
了解侧滑的实现方法
ItemTouchHelper使用步骤:
自定义callback继承 ItemTouchHelper.Callback -> 在内部写一个数据操作接口 -> 重写触发方法调用接口 -> 适配器实现数据操作接口,重新方法 -> acctivity 中附着 recyclerview
创建 MyCallback 继承 ItemTouchHelper.Callback
把数据操作部分抽象成一个接口 AdapterCallback,这个接口可以写在 MyCallback 内
因为ItemTouchHelper在完成触摸的各种动作后,就要对Adapter的数据进行操作,比如侧滑删除操作
自定义 adapter 实现接口MyCallback.AdapterCallback, 重写相关方法
在MainActivity中
1 2
| ItemTouchHelper helper = new ItemTouchHelper(new MyCallback(adapter)); helper.attachToRecyclerView(recyclerView);
|
具体实现:
MyCallback 及内部接口 AdapterCallback
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| 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; }
@Override public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) { int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN; int swipeFlags = ItemTouchHelper.LEFT; return makeMovementFlags(0, swipeFlags); }
@Override public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
return false; }
@Override public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) { switch (direction) { case ItemTouchHelper.LEFT: adapterCallback.remove(viewHolder.getAdapterPosition()); }
} }
|
自定义适配器并实现操作接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| 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配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| 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); DefaultItemAnimator animator = new DefaultItemAnimator(); animator.setRemoveDuration(500); animator.setMoveDuration(500); recyclerView.setItemAnimator(animator); 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
1 2 3 4 5 6 7 8 9 10 11
| ...
@Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu, menu); return true; }
|
重写activity onOptionsItemSelected()方法,监听item点击事件 link
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| ...
@Override public boolean onOptionsItemSelected(MenuItem item) { String msg = ""; switch (item.getItemId()) { case R.id.menu_add: msg = "add"; break; 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,声明必须属性
1 2 3 4 5 6 7 8 9
| 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
1 2 3 4 5 6 7 8 9 10 11
| Intent intent = new Intent(this, ResultActivity.class); intent.putExtra("title", editText.getText().toString()); intent.putExtra("text", editText2.getText().toString());
PendingIntent pIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); builder.setContentIntent(pIntent); NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
|
基于版本构造NotificationChannel
1 2 3 4 5 6
| if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { String channelId = "default"; String channelName = "默认通知"; manager.createNotificationChannel(new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_HIGH)); }
|
发送通知
1 2 3
| manager.notify(count, builder.build()); count++;
|
编程
点击按钮,将输入框中的值,赋值给输出控件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| private EditText editTextUserName; private Button buttonSubmit; private TextView textViewUserName;
------------------------------------
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); } });
|
构造一个带点击实现的单项对话框
单项???????
1 2 3 4 5 6 7 8 9 10 11
| AlertDialog.Builder dialog3 = new AlertDialog.Builder(this); dialog3.setTitle("水果"); dialog3.setItems(arrayFruit, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Toast.makeText(MainActivity.this, arrayFruit[which], Toast.LENGTH_SHORT).show(); } }); dialog3.show();
|
如果是单选就不看这个了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| 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; }
@NonNull @Override public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { Log.i(TAG, "onCreateViewHolder"); 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) { 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); }
@Override public int getItemCount() { return news.size(); }
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); } } }
|