1.整个项目预期的工作量:2020-04-10至本学期项目提交日期 需要完成的主要任务 目前已花费的时间:2020-04-10-2020-04-27 目前已花费的时间为17天 还剩余时间:10天以上
2.团队任务看板照片
任务 |
未完成 |
已完成 |
负责人 |
服务器搭建,准备好开发相关的环境,如数据库等。 |
√ |
王兵兵 |
|
登录注册功能,实现用户注册信息入库,登录时遍历核实信息。 |
√ |
张宏伟 |
|
记录笔记功能,实现用户记录的笔记入库,包括是否公开,插入的图片信息。 |
√ |
朴远东 |
|
查看笔记功能,实现用户浏览自己的笔记,界面上要求实现笔记的删除,对笔记的修改。 |
√ |
朴远东 |
|
分享大厅界面,实现条件搜索,显示笔记,查看笔记,收藏笔记功能,记得刷新按钮。 |
√ |
王兵兵 |
|
收藏笔记功能,显示在分享大厅页面中收藏的笔记,实现对笔记的删除和查看操作。 |
√ |
王兵兵 |
|
“关于我们”写入简易介绍,留下团队邮箱;“意见反馈”添加留言板和提交按钮,信息提交可考虑数据库,或提交到团队邮箱中,核心目的是让开发人员可见。 |
√ |
||
“我的”界面排版,至少完成修改信息界面,实现对用户信息的修改;帮助界面写入APP的使用方法。 |
√ |
||
其他敲定界面补充。 |
√ |
||
团队任务:UI设计 |
√ |
||
选做:增加功能。 |
√ |
3.SCRUM会议照片
4.产品的状态
产品功能:最新做好的功能(登录注册验证,记录笔记,上传笔记和相关照片到云服务器,分享大厅,收藏和查看相关的笔记)
产品界面:
代码:
package com.example.toa; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.RandomAccessFile; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.Set; import android.annotation.SuppressLint; import android.app.Activity; import android.app.AlertDialog; import android.app.AlertDialog.Builder; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.ActivityInfo; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; import android.os.Bundle; import android.os.Environment; import android.provider.MediaStore; import android.text.TextUtils; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.Window; import android.view.WindowManager; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.GridView; import android.widget.ImageView; import android.widget.RadioGroup; import android.widget.SimpleAdapter; import android.widget.SimpleAdapter.ViewBinder; import android.widget.Toast; import org.jetbrains.annotations.NotNull; import okhttp3.Call; import okhttp3.Callback; import okhttp3.MediaType; import okhttp3.MultipartBody; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; import okhttp3.Response; public class MainActivity extends Activity implements MyDialog.OnButtonClickListener, OnItemClickListener{ private MyDialog dialog;// 图片选择对话框 public static final int NONE = 0; public static final int PHOTOHRAPH = 1;// 拍照 public static final int PHOTOZOOM = 2; // 缩放 public static final int PHOTORESOULT = 3;// 结果 public static final String IMAGE_UNSPECIFIED = "image/*"; public static final MediaType PNG = MediaType.parse("image/png"); private GridView gridView; // 网格显示缩略图 private final int IMAGE_OPEN = 4; // 打开图片标记 private String pathImage; // 选择图片路径 private Bitmap bmp; // 导入临时图片 private ArrayList<HashMap<String, Bitmap>> imageItem; private SimpleAdapter simpleAdapter; // 适配器 private EditText note; // 笔记文本 private EditText title; // 标题文本 private EditText kemu; //学科 private Button handin; // 上传按钮 private RadioGroup rg; // 单选按钮组 private boolean judge; // 判定公开/私有 @SuppressLint("SourceLockedOrientationActivity") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); /* * 防止键盘挡住输入框 不希望遮挡设置activity属性 android:windowSoftInputMode="adjustPan" * 希望动态调整高度 android:windowSoftInputMode="adjustResize" */ getWindow().setSoftInputMode( WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); // 锁定屏幕 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); setContentView(R.layout.activity_main); //点击事件 init(); //加载图片与回显 initData(); } private void init() { //声明 note = (EditText)findViewById(R.id.note); gridView = (GridView) findViewById(R.id.gridView); handin = (Button)findViewById(R.id.handin); title = (EditText)findViewById(R.id.title); kemu = (EditText)findViewById(R.id.kemu); rg = (RadioGroup)findViewById(R.id.rg); note.setHorizontallyScrolling(true); gridView.setOnItemClickListener(this); dialog = new MyDialog(this); dialog.setOnButtonClickListener(this); // activity中调用其他activity中组件的方法 LayoutInflater layout = this.getLayoutInflater(); View view = layout.inflate(R.layout.layout_select_photo, null); //单选按钮(公开/私有) rg.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { @Override public void onCheckedChanged(RadioGroup group, int checkedId) { if(checkedId == R.id.ok){ judge = true; }else{ judge = false; } } }); handin.setOnClickListener(new View.OnClickListener() { //是否公开 private String see_str = ""; //正文 private String note_str = ""; //标题 private String title_str = ""; //用户名(登录界面传参获取) private String user_str = "try"; //学科 private String kemu_str = ""; //年级(传参获取) private String year_str = "3"; @Override public void onClick(View v) { if(judge){ see_str = "公开"; }else{ see_str = "私有"; } note_str = note.getText().toString(); title_str = title.getText().toString(); kemu_str = kemu.getText().toString(); //本地Tomcat,注意不能写localhost,写本机的ip地址【连接服务器时记得改地址】 String URL="http://192.168.101.18:8080/CloudNote/CloudServlet"; //URL携带部分参数 URL+="?title="+title_str+"&see="+see_str+"&user="+user_str+"&kemu="+kemu_str+"&year="+year_str; //OKHttp OkHttpClient client = new OkHttpClient(); MultipartBody.Builder builder = new MultipartBody.Builder().setType(MultipartBody.ALTERNATIVE); //文本文件 MediaType NoteType = MediaType.parse("text/html;charset=utf-8"); //设置文件命名前缀部分 String noteuser = user_str+"_note"; File Notefile = doString(note_str,noteuser); builder.addFormDataPart("note",Notefile.getName(),RequestBody.create(NoteType,Notefile)); //图片文件,设定最多上传3张,不包括+号图片。 int pic_i=0; for(HashMap<String,Bitmap>pic:imageItem){ Set<String> set=pic.keySet(); for(String key:set){ if(pic_i==0){ //跳过默认的+号图片 pic_i++; continue; } //取出bitmap,转换成file,上传 else if(pic_i==1){ Bitmap fbm1 = pic.get(key); //设置文件命名前缀部分 String image1user = user_str+"_image1"; //调用方法生成图片文件 File dofile = doImage(fbm1,image1user); builder.addFormDataPart("image1",dofile.getName(),RequestBody.create(PNG,dofile)); pic_i++; } else if(pic_i==2){ Bitmap fbm2 = pic.get(key); //设置文件命名前缀部分 String image2user = user_str+"_image2"; //调用方法生成图片文件 File dofile = doImage(fbm2,image2user); builder.addFormDataPart("image2",dofile.getName(),RequestBody.create(PNG,dofile)); pic_i++; } else if(pic_i==3){ Bitmap fbm3 = pic.get(key); //设置文件命名前缀部分 String image3user = user_str+"_image3"; //调用方法生成图片文件 File dofile = doImage(fbm3,image3user); builder.addFormDataPart("image3",dofile.getName(),RequestBody.create(PNG,dofile)); pic_i++; } } } RequestBody requestBody = builder.build(); Request request = new Request.Builder().url(URL) .post(requestBody) .build(); client.newCall(request).enqueue(new Callback() { @Override public void onFailure(@NotNull Call call, @NotNull IOException e) { Log.i("TRYxxx","连接失败"); e.printStackTrace(); } @Override public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { Log.i("TRYxxx","连接的消息"+response.message()); if(response.isSuccessful()){ Log.i("TRYxxx","连接成功"); } } }); } }); } private void initData() { /* * 载入默认图片添加图片加号 */ bmp = BitmapFactory.decodeResource(getResources(), R.drawable.ic_addpic); // 加号 imageItem = new ArrayList<HashMap<String, Bitmap>>(); HashMap<String, Bitmap> map = new HashMap<String, Bitmap>(); map.put("itemImage", bmp); imageItem.add(map); simpleAdapter = new SimpleAdapter(this, imageItem, R.layout.griditem_addpic, new String[] { "itemImage" }, new int[] { R.id.imageView1 }); simpleAdapter.setViewBinder(new ViewBinder() { @Override public boolean setViewValue(View view, Object data, String textRepresentation) { // TODO Auto-generated method stub if (view instanceof ImageView && data instanceof Bitmap) { ImageView i = (ImageView) view; i.setImageBitmap((Bitmap) data); return true; } return false; } }); gridView.setAdapter(simpleAdapter); } //Bitmap转file public static File doImage(Bitmap fbitmap, String user) { ByteArrayOutputStream fbaos = new ByteArrayOutputStream(); fbitmap.compress(Bitmap.CompressFormat.JPEG,100,fbaos); int options = 100; //判断是否大于20kb,是则继续压缩 while(fbaos.toByteArray().length/1024>20){ fbaos.reset(); options-=10; fbitmap.compress(Bitmap.CompressFormat.JPEG,options,fbaos); long length = fbaos.toByteArray().length; } SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss"); Date date = new Date(System.currentTimeMillis()); String filename = user+"_"+format.format(date); String filepath = Environment.getExternalStorageDirectory().toString()+"/CloudNoteImage"; File pathfile = new File(filepath); if(!pathfile.exists()){ pathfile.mkdir(); } File file = new File(filepath,filename+".png"); //注意创建文件,否则会发生文件读取错误 if(!file.exists()){ try{ file.createNewFile(); }catch (IOException e){ e.printStackTrace(); } } try{ FileOutputStream fos = new FileOutputStream(file); try{ fos.write(fbaos.toByteArray()); fos.flush(); fos.close(); } catch (IOException e) { e.printStackTrace(); } }catch (IOException e){ e.printStackTrace(); } return file; } //将正文信息写入txt文件 public static File doString(String strwrite, String user){ //取时间 SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss"); Date date = new Date(System.currentTimeMillis()); //生成文件名 String filename = user+"_"+format.format(date); String filepath = Environment.getExternalStorageDirectory().toString()+"/CloudNoteTXT"; //创建文件 File pathfile = new File(filepath); if(!pathfile.exists()){ pathfile.mkdir(); } File file = new File(filepath,filename+".txt"); if(!file.exists()){ try{ file.createNewFile(); }catch (IOException e){ e.printStackTrace(); } } try{ FileOutputStream outputStream = new FileOutputStream(file); outputStream.write(strwrite.getBytes()); outputStream.close(); } catch (IOException e) { e.printStackTrace(); } return file; } //调用相机 @Override public void camera() { // TODO Auto-generated method stub Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File( Environment.getExternalStorageDirectory(), "temp.jpg"))); startActivityForResult(intent, PHOTOHRAPH); } //调用相册 @Override public void gallery() { // TODO Auto-generated method stub Intent intent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI); startActivityForResult(intent, IMAGE_OPEN); } @Override public void cancel() { // TODO Auto-generated method stub dialog.cancel(); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { // TODO Auto-generated method stub super.onActivityResult(requestCode, resultCode, data); if (resultCode == NONE) return; // 拍照 if (requestCode == PHOTOHRAPH) { // 设置文件保存路径这里放在跟目录下 File picture = new File(Environment.getExternalStorageDirectory() + "/temp.jpg"); startPhotoZoom(Uri.fromFile(picture)); } if (data == null) return; // 处理结果 if (requestCode == PHOTORESOULT) { Bundle extras = data.getExtras(); if (extras != null) { Bitmap photo = extras.getParcelable("data"); ByteArrayOutputStream stream = new ByteArrayOutputStream(); photo.compress(Bitmap.CompressFormat.JPEG, 75, stream);// (0-100)压缩文件 // 将图片放入gridview中 HashMap<String, Bitmap> map = new HashMap<String, Bitmap>(); map.put("itemImage", photo); imageItem.add(map); simpleAdapter = new SimpleAdapter(this, imageItem, R.layout.griditem_addpic, new String[] { "itemImage" }, new int[] { R.id.imageView1 }); simpleAdapter.setViewBinder(new ViewBinder() { @Override public boolean setViewValue(View view, Object data, String textRepresentation) { // TODO Auto-generated method stub if (view instanceof ImageView && data instanceof Bitmap) { ImageView i = (ImageView) view; i.setImageBitmap((Bitmap) data); return true; } return false; } }); gridView.setAdapter(simpleAdapter); simpleAdapter.notifyDataSetChanged(); dialog.dismiss(); } } // 打开图片 if (resultCode == RESULT_OK && requestCode == IMAGE_OPEN) { startPhotoZoom(data.getData()); } super.onActivityResult(requestCode, resultCode, data); } @Override protected void onResume() { // TODO Auto-generated method stub super.onResume(); //判定有图片添加 if (!TextUtils.isEmpty(pathImage)) { //bitmap回显 Bitmap addbmp = BitmapFactory.decodeFile(pathImage); //将信息存入Map中 HashMap<String, Bitmap> map = new HashMap<String, Bitmap>(); map.put("itemImage", addbmp); imageItem.add(map); //在griditem_addpic.xml中向imageView1添加图片 simpleAdapter = new SimpleAdapter(this, imageItem, R.layout.griditem_addpic, new String[] { "itemImage" }, new int[] { R.id.imageView1 }); simpleAdapter.setViewBinder(new ViewBinder() { @Override public boolean setViewValue(View view, Object data, String textRepresentation) { // TODO Auto-generated method stub if (view instanceof ImageView && data instanceof Bitmap) { ImageView i = (ImageView) view; i.setImageBitmap((Bitmap) data); return true; } return false; } }); gridView.setAdapter(simpleAdapter); simpleAdapter.notifyDataSetChanged(); // 刷新后释放防止手机休眠后自动添加 pathImage = null; dialog.dismiss(); } } @Override public void onItemClick(AdapterView<?> parent, View v, int position, long id) { // TODO Auto-generated method stub if (imageItem.size() == 4&&position==0) { // 第一张为默认图片,点击+号时才判定是否已满 Toast.makeText(MainActivity.this, "图片数3张已满", Toast.LENGTH_SHORT).show(); } else if (position == 0) { // 点击图片位置为+ 0对应0张图片 // 选择图片 dialog.show(); // 通过onResume()刷新数据 } else { dialog(position); } } /* * Dialog对话框提示用户删除操作 position为删除图片位置 */ protected void dialog(final int position) { AlertDialog.Builder builder = new Builder(MainActivity.this); builder.setMessage("确认移除已添加图片吗?"); builder.setTitle("提示"); builder.setPositiveButton("确认", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); imageItem.remove(position); simpleAdapter.notifyDataSetChanged(); } }); builder.setNegativeButton("取消", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); } }); builder.create().show(); } public void startPhotoZoom(Uri uri) { Intent intent = new Intent("com.android.camera.action.CROP"); intent.setDataAndType(uri, IMAGE_UNSPECIFIED); intent.putExtra("crop", "true"); // aspectX aspectY 是宽高的比例 intent.putExtra("aspectX", 1); intent.putExtra("aspectY", 1); // outputX outputY 是裁剪图片宽高 intent.putExtra("outputX", 64); intent.putExtra("outputY", 64); intent.putExtra("return-data", true); startActivityForResult(intent, PHOTORESOULT); } }
测设用例:
不同设备测试和数据库存储路径和照片查看