要求
1:APK要运用到 Activity 和广播、服务 知识点
2:布局要求最优,UI不做要求
3:开发过程中,要学会使用log定位问题
4:此APK要有桌面小组件,功能入口不做要求
最终效果(登录页面用户名&密码有意写上):
MainActivity
package com.example.memoapplication;
import static java.lang.Integer.parseInt;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import com.example.memoapplication.service.MemoService;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
RecyclerView recyclerView;
private RecyclerViewAdapter recyclerViewAdapter;
private ArrayList<String> memos = new ArrayList<>();
private static final String TAG = "yx";
public static final String ADD_MEMO = "com.example.myapplication.add";
private int count = 1;
private ArrayList<String> load;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = findViewById(R.id.recycler);
recyclerViewAdapter = new RecyclerViewAdapter(MainActivity.this, memos);
recyclerView.setAdapter(recyclerViewAdapter);
recyclerView.setLayoutManager(new LinearLayoutManager(MainActivity.this));
findViewById(R.id.delAll_memo_button).setOnClickListener(this);
findViewById(R.id.del_one_button).setOnClickListener(this);
Log.d(TAG,"主线程线程ID:"+Thread.currentThread().getId());
//加载历史备忘录数据
Log.d(TAG, "开始加载历史数据");
//开启线程 读取历史数据
callAbleLoad callAbleLoad = new callAbleLoad();
FutureTask<ArrayList<String>> arrayListFutureTask = new FutureTask<>(callAbleLoad);
new Thread(arrayListFutureTask).start();
try {
load = arrayListFutureTask.get();
} catch (ExecutionException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
for (String s : load) {
memos.add(s);
//添加默认的分割线
recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
}
//若集合为不为空 则说明历史有数据 编号应该紧接历史数据
if (!memos.isEmpty()) {
Log.d(TAG, "编号应该紧接历史数据");
count = parseInt(getNumber(load.get(load.size() - 1))) + 1;
}
//新增
Button addMemoButton = findViewById(R.id.add_memo_button);
addMemoButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//增加Dialog
showAddMemoDialog();
}
});
}
//新增备忘录
private void showAddMemoDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
final EditText input = new EditText(this);
builder.setTitle("新增备忘录")
.setCancelable(false)
.setView(input)
.setPositiveButton("新增", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//获取新增的备忘录 去除两端空白
String startMemo = input.getText().toString().trim();
//若输入为空,全是空格,提示输入为空,不添加
if (startMemo.replaceAll(" ", "").isEmpty()) {
Toast.makeText(MainActivity.this, "输入为空,添加失败!", Toast.LENGTH_SHORT).show();
} else {
String memo = count + " " + startMemo;
//添加进集合
memos.add(memo);
//添加默认的分割线
recyclerView.addItemDecoration(new DividerItemDecoration(MainActivity.this, DividerItemDecoration.VERTICAL));
count++;
//通知数据发生更改
recyclerViewAdapter.notifyDataSetChanged();
//启动service 增加数据到文件
Intent intent = new Intent(MainActivity.this, MemoService.class);
intent.setAction(ADD_MEMO);
intent.putExtra("memo", memo);
startService(intent);
}
}
});
builder.setNegativeButton("取消", null);
builder.show();
}
//清空所有备忘录数据
@Override
public void onClick(View v) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
if (v.getId() == R.id.delAll_memo_button) {
//先查看备忘录是否有数据
if (memos.isEmpty()) {
Toast.makeText(MainActivity.this, "备忘录中暂无数据", Toast.LENGTH_SHORT).show();
} else {
builder.setTitle("警告!")
.setMessage("你确定要清空所有备忘录吗?")
.setCancelable(false)
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//清空
del();
//刷新页面
refresh();
}
});
builder.setNegativeButton("取消", null);
builder.show();
}
} else if (v.getId() == R.id.del_one_button) {
//先查看备忘录是否有数据
if (memos.isEmpty()) {
Toast.makeText(MainActivity.this, "备忘录中暂无数据", Toast.LENGTH_SHORT).show();
} else {
final EditText edit = new EditText(this);
builder.setView(edit)
.setTitle("删除备忘录")
.setMessage("请输入要删除的数据编号")
.setCancelable(false)
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Log.d(TAG, "获取到的要删除的编号:" + edit.getText());
if (!memos.isEmpty()) {
Log.d(TAG, "memos not null");
//有无此编号的数据
boolean flag = true;
for (int i = 0; i < memos.size(); i++) {
if (getNumber(memos.get(i)).equals(edit.getText() + "")) {
//先将memos集合中的目标数据删除,再清空文件中的全部数据,再将memos中的数据写回到文件
//将memos集合中的目标数据删除
memos.remove(i);
Log.d(TAG, "找到目标数据" + i + " " + memos.size());
//清空文件中的全部数据
del();
Log.d(TAG, "memos大小" + memos.size());
for (int j = 0; j < memos.size(); j++) {
Intent intent = new Intent(MainActivity.this, MemoService.class);
intent.setAction(ADD_MEMO);
intent.putExtra("memo", memos.get(j));
startService(intent);
}
//刷新页面
refresh();
//找到编号数据
flag = false;
break;
}
}
//若没找到此编号的数据则提示
if (flag) {
Toast.makeText(MainActivity.this, "输入的编号有误或不存在!", Toast.LENGTH_SHORT).show();
}
}
}
});
builder.setNegativeButton("取消", null);
builder.show();
}
}
}
//创建线程读取文件中的数据 耗时操作
class callAbleLoad implements Callable<ArrayList<String>> {
@Override
public ArrayList<String> call() throws Exception {
Log.d(TAG,"callable线程ID:"+Thread.currentThread().getId());
FileInputStream in = null;
BufferedReader reader = null;
ArrayList<String> list = new ArrayList<>();
try {
//找到名input的文件
in = openFileInput("input");
reader = new BufferedReader(new InputStreamReader(in));
String line = "";
//一行一行读取文件内容,放入ArrayList
while ((line = reader.readLine()) != null) {
list.add(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
for (int i = 0; i < list.size(); i++) {
Log.d(TAG, "读取到的第" + (i + 1) + "行数据:" + list.get(i));
}
return list;
}
}
//清空所有备忘录数据
private void del() {
deleteFile("input");
}
//获取备忘录编号
private String getNumber(String s) {
int i = s.indexOf(" ");
Log.d(TAG, "读取到的第:" + s.substring(0, i));
return s.substring(0, i);
}
//刷新页面
private void refresh() {
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
}
}
BootReceiver
package com.example.memoapplication.receiver;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import com.example.memoapplication.LoginActivity;
public class BootReceiver extends BroadcastReceiver {
private static final String TAG = "yx";
@Override
public void onReceive(Context context, Intent intent) {
if (intent != null && intent.getAction().equals(LoginActivity.SEND_BROADCAST)){
Log.d(TAG,"³É¹¦ÊÕµ½¹ã²¥");
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle("¾¯¸æ!")
.setMessage("Õ˺ŻòÃÜÂë´íÎó£¡")
.setCancelable(false)
.setPositiveButton("OK",null);
builder.show();
}
}
}
MemoService
package com.example.memoapplication.service;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
import androidx.annotation.Nullable;
import com.example.memoapplication.MainActivity;
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
public class MemoService extends Service {
private static final String TAG = "yx";
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent != null && intent.getAction().equals(MainActivity.ADD_MEMO)) {
String memo = intent.getStringExtra("memo");
if (memo != null) {
Log.d(TAG,"添加");
// 备忘录数据保存到本地
save(memo);
}
}
return START_NOT_STICKY;
}
private void save(String input){
FileOutputStream outputStream = null;
BufferedWriter writer = null;
try {
outputStream = openFileOutput("input", Context.MODE_APPEND);
writer = new BufferedWriter(new OutputStreamWriter(outputStream));
writer.write(input);
writer.write("\r\n");
}catch (IOException e){
e.printStackTrace();
}finally {
if (writer != null){
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
RecyclerViewAdapter备忘录主体滚动展示
package com.example.memoapplication;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.MyViewHolder> {
Context context;
ArrayList<String> memosList;
public RecyclerViewAdapter(Context context, ArrayList<String> memosList) {
this.context = context;
this.memosList = memosList;
}
@NonNull
@Override
public RecyclerViewAdapter.MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(this.context).inflate(R.layout.recycierview_item, parent, false);
return new MyViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
holder.textView.setText(memosList.get(position));
}
@Override
public int getItemCount() {
return this.memosList.size();
}
public static class MyViewHolder extends RecyclerView.ViewHolder{
TextView textView;
public MyViewHolder(@NonNull View itemView) {
super(itemView);
textView = itemView.findViewById(R.id.memo);
}
}
}
NewAppWidget 桌面小组件
package com.example.memoapplication;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
import android.widget.RemoteViews;
/**
* Implementation of App Widget functionality.
*/
public class NewAppWidget extends AppWidgetProvider {
static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
int appWidgetId) {
CharSequence widgetText = context.getString(R.string.appwidget_text);
// Construct the RemoteViews object
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.new_app_widget);
views.setTextViewText(R.id.appwidget_text, widgetText);
Intent intent = new Intent(context, LoginActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE);
views.setOnClickPendingIntent(R.id.appwidget_text,pendingIntent);
// Instruct the widget manager to update the widget
appWidgetManager.updateAppWidget(appWidgetId, views);
}
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
// There may be multiple widgets active, so update all of them
for (int appWidgetId : appWidgetIds) {
updateAppWidget(context, appWidgetManager, appWidgetId);
}
}
@Override
public void onEnabled(Context context) {
// Enter relevant functionality for when the first widget is created
}
@Override
public void onDisabled(Context context) {
// Enter relevant functionality for when the last widget is disabled
}
}
Loginctivity 登录
package com.example.memoapplication;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import com.example.memoapplication.receiver.BootReceiver;
public class LoginActivity extends AppCompatActivity implements View.OnClickListener {
public static final String SEND_BROADCAST = "com.example.myapplication.send";
private static final String TAG = "yx";
private EditText usernameEdit;
private EditText passwordEdit;
private BootReceiver bootReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
usernameEdit = findViewById(R.id.username);
passwordEdit = findViewById(R.id.password);
findViewById(R.id.login).setOnClickListener(this);
}
@Override
public void onClick(View view) {
String username = usernameEdit.getText().toString();
String password = passwordEdit.getText().toString();
if (username.equals("1") && password.equals("1")){
Log.d(TAG,"账号密码验证通过");
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}else {
Log.d(TAG,"账号密码验证失败,,发送广播,警告弹窗!");
Intent intent = new Intent(SEND_BROADCAST);
sendBroadcast(intent);
}
}
@Override
protected void onStart() {
super.onStart();
Log.d(TAG,"广播注册");
bootReceiver = new BootReceiver();
IntentFilter filter = new IntentFilter(SEND_BROADCAST);
registerReceiver(bootReceiver,filter);
}
@Override
protected void onStop() {
super.onStop();
Log.d(TAG,"广播注销");
unregisterReceiver(bootReceiver);
}
}
布局文件:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="25dp"
android:text="备忘录"
android:gravity="center"
android:textSize="20sp"
android:textStyle="bold"
android:background="#b9b900"
android:textColor="@color/white"
tools:ignore="MissingConstraints" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
android:layout_marginStart="10dp"
android:layout_marginTop="30dp"
android:layout_marginEnd="10dp"
android:layout_marginBottom="60dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:ignore="MissingConstraints"
android:paddingTop="680dp">
<Button
android:id="@+id/delAll_memo_button"
android:layout_height="wrap_content"
android:layout_width="0dp"
android:layout_weight="1"
android:textColor="#f81c00"
android:textSize="17sp"
android:textStyle="bold"
android:text="清空所有" />
<Button
android:id="@+id/del_one_button"
android:layout_height="wrap_content"
android:layout_width="0dp"
android:layout_weight="1"
android:textColor="#fdff26"
android:textSize="17sp"
android:textStyle="bold"
android:text="删除" />
<Button
android:id="@+id/add_memo_button"
android:layout_height="wrap_content"
android:layout_width="0dp"
android:layout_weight="1"
android:textColor="#00f10a"
android:textSize="17sp"
android:textStyle="bold"
android:text="新增" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
recycierview_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:background="@drawable/item_background"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="10dp">
<TextView
android:id="@+id/memo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:textSize="20sp" />
</LinearLayout>
new_app_widget.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/Widget.MemoApplication.AppWidget.Container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:theme="@style/Theme.MemoApplication.AppWidgetContainer">
<TextView
android:id="@+id/appwidget_text"
style="@style/Widget.MemoApplication.AppWidget.InnerView"
android:layout_width="200dp"
android:layout_height="100dp"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:layout_margin="8dp"
android:contentDescription="@string/appwidget_text"
android:text="@string/appwidget_text"
android:textSize="24sp"
android:textStyle="bold|italic" />
</RelativeLayout>
activity_login.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".LoginActivity"
android:orientation="vertical"
android:paddingTop="100dp">
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:text="登录备忘录"
android:gravity="center"
android:textSize="25sp"/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="60dp"
android:paddingRight="25dp">
<TextView
android:layout_width="100dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:textSize="18sp"
android:text="用户名:"
android:paddingLeft="25dp"/>
<EditText
android:id="@+id/username"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="center_vertical"/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="60dp"
android:paddingRight="25dp">
<TextView
android:layout_width="100dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:textSize="18sp"
android:text="密 码:"
android:paddingLeft="25dp"/>
<EditText
android:id="@+id/password"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="center_vertical"
android:inputType="textPassword"/>
</LinearLayout>
<Button
android:id="@+id/login"
android:layout_width="200dp"
android:layout_height="60dp"
android:text="登 录"
android:layout_gravity="center"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="用户名:1 密码:1"
android:gravity="center"/>
</LinearLayout>
drawable/item_background.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#ffc10e"/>
<corners android:radius="4dp"/>
</shape>
总结:
主要功能:登录备忘录、历史备忘录展示、展示台滚动、新增备忘录、删除指定编号备忘录,清空所有备忘录、不允许新增空文本、桌面小组件
运用知识:Activity、Broadcast、Service、RecyclerView、文件的读取和写入、Callable、字符串的操作、AlertDialog、Widge