第二次作业要求做一个简单的小APP备忘录。可以添加删除更改recyclerView(也就是我们的备忘录)。
总的思路大概是先设计3个activity的xml文件,menu和recyclerView要多设计几个xml以便联系到一起。然后结合一些固定不变的代码和套路复制粘贴补全。
recyclerView是用arrayList实现的。安卓项目开始创建的时候,系统会自动帮我们加载arrayList到recyclerView里面。此后,每当attayList有变化,我们得手动通知程序重新加载arrayList到recyclerView里面去。具体方法是用mAdapter.notifyDataSetChanged(); 来通知Adapter重新加载。Adapter是recyclerView要求的几个类之一,负责将arrayList加载到recyclerView里面去。具体是通过三个方法(最后一个目前还不是十分清楚意义):第一是动态的管理view(对我这个APP来说就是大方框);第二是结合viewHolder将arrayList中的文字写道view(大方框,有单独的xml格式layout)里面去;第三个是返回arrayList里面的元素个数。ViewHolder也是要求的第二个类,作用是简化了繁琐的代码,通过它可以找到对应的view,不用一遍遍输入。然后很重要的一个问题是,当你选中一个方框时,你必须知道这个方框在arrayList的什么位置,否则你无法对它进行删除,编辑操作。其实很简单,用一个简单的函数,recyclerView.getChildAdapterPosition(v); 就可以找到这个位置。我这个备忘录的编辑和删除功能正式基于这一点。
Json是文本格式,通过一些特定的读写操作,我们可以在onPause或onStop哪儿的将安卓应用当前的信息记录进去,保存到硬盘或者或联网等等,这样下次再进入安卓的时候,我们在onCreat或onStart或onResume哪儿的里面先将Json导入一下,就可以避免因为onStop,onDestory导致的信息丢失。如果没有Json文件来保存信息,备忘录根本一点意义没有。
AsycnTask是安卓提供的一个类,提供多线程服务。一般导入轻量级的数据,比如Json数据之类的。在onCreate或合适的地方用一下。它通过三个泛型规范了传入类型,进度完成报告采用类型,返回类型。第二个类型为了增强用户互动,具体以后再根据规范详细看。
最后还有就是activity之间转移和信息的传递。是通过intent。intent可以将一个activity转移到另外一个activity,通过startActivity,当然,原来的activity一般就被onStop了。另外intent通过putExtra方法可作为activity之间信息传递的介质,保存一些东西进去,在另一个activity中按照“KEY”提取。然后该父activity也可以选择要不要接收子activity返回的数据,具体是通过startActivityForResult实现的。子Activity返回的数据在父Activity里面通过onActivityResult方法接收。我这个备忘录的编辑功能正式基于这一点。
mainfests: 记录全部的activity,设置好mainActivity。
EditActivity,点一个recyclerView就进入此activity,这里面返回键要重写,要显示dialog,让用户选择是保存还是放弃。还要在右上角有保存menu。
package com.example.multinotes;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.method.ScrollingMovementMethod;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.EditText;
import android.widget.Toast;
public class EditActivity extends AppCompatActivity {
private EditText title_Edit;
private EditText content_Edit;
private static final String TAG = "EditActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_edit);
title_Edit = findViewById(R.id.title);
content_Edit = findViewById(R.id.content);
content_Edit.setMovementMethod(new ScrollingMovementMethod());
content_Edit.setTextIsSelectable(true);
title_Edit.setText(getIntent().getStringExtra("title"));
content_Edit.setText(getIntent().getStringExtra("content"));
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.action_menu2, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if(item.getItemId() == R.id.save) {
//go to an activity that display version and author information.
Intent data = new Intent();
data.putExtra("title", title_Edit.getText().toString());
data.putExtra("content", content_Edit.getText().toString());
setResult(RESULT_OK, data);
finish();
return true;
}
else
return super.onOptionsItemSelected(item);
}
@Override
public void onBackPressed() {
if(content_Edit.getText().toString().equals("") && title_Edit.getText().toString().equals("")){
showToast();
super.onBackPressed();
}
else {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
//builder.setIcon(R.drawable.icon1);
builder.setPositiveButton("Save", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
//position = mAdapter.getPosition();
Intent data = new Intent();
data.putExtra("title", title_Edit.getText().toString());
data.putExtra("content", content_Edit.getText().toString());
setResult(RESULT_OK, data);
finish();
}
});
builder.setNegativeButton("Discard", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id)
{
showToast();
finish();
}
});
builder.setMessage("Your note is not saved!\n" + "Do you want to save '" + title_Edit.getText().toString() + "'?");
builder.setTitle("Notice");
AlertDialog dialog = builder.create();
dialog.show();
}
}
public void showToast(){
Toast.makeText(this, "Data isn't saved!", Toast.LENGTH_SHORT).show();
}
}
Information Adaptor,recyclerView 要求的类之一,里面需要重写3个方法,一个负责创造动态的recyclerView,一个要跟ViewHolder联系起来,设置文本内容,一个显示ArrayList里面有多少元素。
package com.example.multinotes;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
public class InformationAdapter extends RecyclerView.Adapter
{
private static final String TAG = "InformationAdapter";
private ArrayList noteList;
private MainActivity mainAct;
public InformationAdapter(ArrayList noteList, MainActivity ma) {
this.noteList = noteList;
mainAct = ma;
}
@Override
public MyViewHolder onCreateViewHolder(final ViewGroup parent, int viewType) {
Log.d(TAG, "onCreateViewHolder: MAKING NEW");
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.information, parent, false);
itemView.setOnClickListener(mainAct);
itemView.setOnLongClickListener(mainAct);
return new MyViewHolder(itemView);
}
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
Log.d(TAG, "onBindViewHolder: " + position);
Note note = noteList.get(position);
holder.title.setText(note.getTitle());
holder.date.setText(note.getDate());
String s = note.getContent();
int len = s.length();
if(len > 80){
s = s.substring(0, 80);
s += "...";
}
holder.content.setText(s);
}
@Override
public int getItemCount() {
return noteList.size();
}
}
ViewHolder:
package com.example.multinotes;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.TextView;
public class MyViewHolder extends RecyclerView.ViewHolder {
public TextView title;
public TextView date;
public TextView content;
public MyViewHolder(View view) {
super(view);
title = (TextView) view.findViewById(R.id.title);
date = (TextView) view.findViewById(R.id.date);
content = (TextView) view.findViewById(R.id.content);
}
}
自己建立的Note类,标题内容创建时间,arrayList的内容。
package com.example.multinotes;
import java.util.Date;
public class Note
{
public String content;
public String title;
private String date;
public Note(String title, String content)
{
this.title = title;
this.content = content;
date = setDate();
}
private String setDate()
{
Date date = new Date();
return date.toString();
}
public String getContent() { return content; }
public String getTitle() { return title; }
public String getDate() { return date; }
public String toString(){ return title + date; }
}
版本信息:
package com.example.multinotes;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class VersionActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_version);
}
}
MainActivity,比较复杂,结合了menu,Jason,start,onActivityResult等等
package com.example.multinotes;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.AsyncTask;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.JsonReader;
import android.util.JsonWriter;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity
implements View.OnClickListener, View.OnLongClickListener {
private ArrayList noteList = new ArrayList<>();
private RecyclerView recyclerView;
private InformationAdapter mAdapter;
private static final int REQUEST_CODE_1 = 1;
private static final int REQUEST_CODE_2 = 0;
static int i = 0;
private int position;
private static final String TAG = "MainActivity";
//AsyncTask.
private class MyAsyncTask extends AsyncTask
{
@Override
protected Void doInBackground(Void... voids) {
noteList = loadFile();
return null;
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d(TAG, "onCreate: " + i++);
recyclerView = findViewById(R.id.recycler);
mAdapter = new InformationAdapter(noteList, this);
recyclerView.setAdapter(mAdapter);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.addItemDecoration(new DividerItemDecoration(this,
DividerItemDecoration.VERTICAL));
onResult();
mAdapter.notifyDataSetChanged();
}
//Loading Json data by AsyncTask.
private void onResult() { new MyAsyncTask().execute(); }
//Save data to Json file.
@Override
protected void onPause() {
saveNote();
super.onPause();
}
//Add a new item to the head of ArrayList.
private void addItem(Note note)
{
noteList.add(0, note);
mAdapter.notifyDataSetChanged();
}
//Remove specific "position"th item form ArrayList.
private void removeItem(int position)
{
noteList.remove(position);
mAdapter.notifyDataSetChanged();
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
String title;
String content;
if (requestCode == REQUEST_CODE_1) {
if (resultCode == RESULT_OK) {
title = data.getStringExtra("title"); // Get String from EditActivity
content = data.getStringExtra("content");
addItem(new Note(title, content)); // Add new item to ArrayList.
}
else {
Log.d(TAG, "onActivityResult: result Code: " + resultCode);
}
}
else if(requestCode == REQUEST_CODE_2)
{
if (resultCode == RESULT_OK)
{
title = data.getStringExtra("title");
content = data.getStringExtra("content");
noteList.get(position).title = title;
noteList.get(position).content = content;
mAdapter.notifyDataSetChanged();
}
}
else {
Log.d(TAG, "onActivityResult: Request Code " + requestCode);
}
}
@Override
public void onClick(View v)
{
//go to another activity, that activity will allow use edit noteList.
position = recyclerView.getChildAdapterPosition(v);
Intent intent = new Intent(MainActivity.this, EditActivity.class);
String temp_title = noteList.get(position).getTitle();
String temp_content = noteList.get(position).getContent();
intent.putExtra("title", temp_title);
intent.putExtra("content", temp_content);
startActivityForResult(intent, REQUEST_CODE_2);
}
@Override
public boolean onLongClick(View v) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
position = recyclerView.getChildAdapterPosition(v);
//builder.setIcon(R.drawable.icon1);
builder.setPositiveButton("DELETE", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
//position = mAdapter.getPosition();
removeItem(position);
}
});
builder.setNegativeButton("CANCEL", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
}
});
builder.setMessage("Do you want to delete '"+noteList.get(position).title+"'?");
builder.setTitle("Warning");
AlertDialog dialog = builder.create();
dialog.show();
return false;
}
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.action_menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
Log.d(TAG, "onOptionsItemSelected: " + i++);
switch (item.getItemId()) {
case R.id.version:
//go to an activity that display version and author information.
Intent intent1 = new Intent(MainActivity.this, VersionActivity.class);
startActivity(intent1);
return true;
case R.id.addElement:
//go to another activity, and that activity allows user add a new Note class into noteList.
Intent intent2 = new Intent(MainActivity.this, EditActivity.class);
startActivityForResult(intent2, REQUEST_CODE_1);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
private void saveNote() {
Log.d(TAG, "saveNote: Saving JSON File");
try {
FileOutputStream fos = getApplicationContext().openFileOutput(getString(R.string.file_name), Context.MODE_PRIVATE);
JsonWriter writer = new JsonWriter(new OutputStreamWriter(fos, getString(R.string.encoding)));
writer.setIndent(" ");
writer.beginObject();
for(int i = 0; i < noteList.size(); i++)
{
writer.name("title").value(noteList.get(i).title);
writer.name("content").value(noteList.get(i).content);
}
writer.endObject();
writer.close();
} catch (Exception e) {
e.getStackTrace();
}
}
private ArrayList loadFile() {
Log.d(TAG, "loadFile: Loading JSON File");
ArrayList title_holder = new ArrayList<>();
ArrayList content_holder = new ArrayList<>();
try {
InputStream is = getApplicationContext().openFileInput(getString(R.string.file_name));
JsonReader reader = new JsonReader(new InputStreamReader(is, getString(R.string.encoding)));
reader.beginObject();
while (reader.hasNext()) {
String name = reader.nextName();
if (name.equals("title")) {
title_holder.add(reader.nextString());
} else if (name.equals("content")) {
content_holder.add(reader.nextString());
} else {
reader.skipValue();
}
}
reader.endObject();
} catch (FileNotFoundException e) {
Toast.makeText(this, getString(R.string.no_file), Toast.LENGTH_SHORT).show();
} catch (Exception e) {
e.printStackTrace();
}
for(int i = 0; i < title_holder.size(); i++)
noteList.add(new Note(title_holder.get(i), content_holder.get(i)));
return noteList;
}
}
activity_edit.xml
activity_main.xml
activity_version.xml
information.xml
action_menu.xml
action_menu2.xml