最近刚看完了《第一行代码》这本书,趁着手热,写了一个日记本App,App虽然挺简单的,但对于一个刚学Android开发的小白来说,开发起来还是很艰难的,现在项目已经完成,在这里跟大家分享下,希望一起交流进步!

先上图,顺便介绍下功能:

这是登录界面,简洁明了,不多说了哈

这是登录之后的主界面,采用的是卡片式布局

这是编辑界面

当长按时日记右上方会出现复选框,选中点击删除按钮即可进行删除操作

好了,功能介绍完毕,下面介绍下主要的实现方法吧


LoignActivity(实现登录操作):

package com.example.a15711.diarypractice;import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.mainactivity.MainActivity;
import android.os.Build;
import android.preference.PreferenceManager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.telephony.CellIdentityCdma;
import android.text.method.HideReturnsTransformationMethod;
import android.text.method.PasswordTransformationMethod;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.Toast;public class LoginActivity extends AppCompatActivity{private SharedPreferences pref;//定义一个SharedPreferences对象private SharedPreferences.Editor editor;//调用SharedPreferences对象的edit()方法来获取一个SharedPreferences.Editor对象,用以添加要保存的数据private Button login;//登录按钮private EditText adminEdit;//用户名输入框private EditText passwordEdit;//密码输入框private CheckBox savePassword;//是否保存密码复选框private CheckBox showPassword;//显示或隐藏密码复选框@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//将背景图与状态栏融合到一起,只支持Android5.0以上的版本if(Build.VERSION.SDK_INT>=21){View decorView=getWindow().getDecorView();//布局充满整个屏幕decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN|View.SYSTEM_UI_FLAG_LAYOUT_STABLE);//设置状态栏为透明getWindow().setStatusBarColor(Color.TRANSPARENT);}setContentView(R.layout.activity_login);//获取各组件或对象的实例pref= PreferenceManager.getDefaultSharedPreferences(this);login=(Button)findViewById(R.id.login_button);adminEdit=(EditText)findViewById(R.id.admin);passwordEdit=(EditText)findViewById(R.id.password);savePassword=(CheckBox)findViewById(R.id.save_password);showPassword=(CheckBox)findViewById(R.id.show_password);//获取当前“是否保存密码”的状态final boolean isSave=pref.getBoolean("save_password",false);//当“是否保存密码”勾选时,从SharedPreferences对象中读出保存的内容,并显示出来if(isSave){String account=pref.getString("account","");String password=pref.getString("password","");adminEdit.setText(account);passwordEdit.setText(password);//把光标移到文本末尾处adminEdit.setSelection(adminEdit.getText().length());passwordEdit.setSelection(passwordEdit.getText().length());savePassword.setChecked(true);}//用户点击登录时的处理事件login.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {//读出用户名和密码并判断是否正确String account=adminEdit.getText().toString();String password=passwordEdit.getText().toString();//用户名和密码正确if(account.equals("admin")&&password.equals("123456")){editor=pref.edit();//“是否保存密码”勾选if(savePassword.isChecked()){editor.putBoolean("save_password",true);editor.putString("account",account);editor.putString("password",password);}else{editor.clear();}//提交完成数据存储editor.apply();//显示登录成功并跳转到主界面活动Toast.makeText(LoginActivity.this,"登录成功",Toast.LENGTH_SHORT).show();Intent intent=new Intent(LoginActivity.this, MainActivity.class);startActivity(intent);//结束当前活动finish();}//用户名或密码错误else{Toast.makeText(LoginActivity.this,"登录失败,请重新输入!",Toast.LENGTH_SHORT).show();}}});//用户点击'显示密码'复选框showPassword.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if(showPassword.isChecked()){showOrHide(passwordEdit,true);}else{showOrHide(passwordEdit,false);}}});}//当用户离开活动时,检测是否勾选记住密码,若勾选则保存用户输入的用户名及密码@Overrideprotected void onDestroy() {super.onDestroy();editor=pref.edit();String account=adminEdit.getText().toString();String password=passwordEdit.getText().toString();if(savePassword.isChecked()){editor.putBoolean("save_password",true);editor.putString("account",account);editor.putString("password",password);}else{editor.clear();}editor.apply();}//显示或隐藏密码private void showOrHide(EditText passwordEdit,boolean isShow){//记住光标开始的位置int pos = passwordEdit.getSelectionStart();if(isShow){passwordEdit.setTransformationMethod(HideReturnsTransformationMethod.getInstance());}else{passwordEdit.setTransformationMethod(PasswordTransformationMethod.getInstance());}passwordEdit.setSelection(pos);}}

这个活动挺简单的,要注意的地方不多,一个是SharedPreferences ,用它来保存用户名和密码比较方便;另一个就是showOrHide方法,用它来实现显示或隐藏密码,这里初始密码为隐藏状态,需要在布局中密码属性中设置:android:password=“true”;

MainActivity:(日记展示界面)

package android.mainactivity;import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.editactivity.EditActivity;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.Toast;
import com.example.a15711.diarypractice.R;
import org.litepal.crud.DataSupport;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;public class MainActivity extends AppCompatActivity {//要删除的项的标志HashMap数组HashMap<Integer,String> str=new HashMap<>();//设置是否在onStart中更新数据源的标志private int update=0;//存储diary对象的数组private List<diary> diaryList=new ArrayList<>();//适配器private diaryAdapter adapter;//是否处于多选删除状态// 设置这个变量是为了让区分正常点击和多选删除时的点击事件//以及长按状态时不再响应长按事件private boolean isDeleteState=false;//网格布局管理器GridLayoutManager gridLayoutManager=new GridLayoutManager(this,2);//线性布局管理器LinearLayoutManager linearLayoutManager=new LinearLayoutManager(this);@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//从数据库中获取日记diaryList= DataSupport.findAll(diary.class);RecyclerView recyclerView=(RecyclerView)findViewById(R.id.recycler_view);if(diaryList.size()>0){//数据库中有日记记录,以网格布局展示recyclerView.setLayoutManager(gridLayoutManager);}else{//数据库中没有日记记录,用线性布局显示“无数据”recyclerView.setLayoutManager(linearLayoutManager);}//适配器的初始化,第一个参数传入数据源,第二个参数false表示正常状态;true表示多选删除状态adapter=new diaryAdapter(diaryList,false);recyclerView.setAdapter(adapter);//初始化‘新建’和‘删除’按钮Button build=(Button)findViewById(R.id.build_button);Button delete=(Button)findViewById(R.id.delete_button);//点击‘新建’按钮时,跳转到编辑日记的活动build.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Intent intent=new Intent(MainActivity.this, EditActivity.class);intent.putExtra("diaryContent","");//传入日记内容,这里为空intent.putExtra("signal",0);//传入‘新建’标志:0startActivity(intent);}});//点击‘删除按钮时,执行删除操作delete.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {deleteSelections();}});//为RecyclerView添加点击事件响应和长按事件响应recyclerView.addOnItemTouchListener(new RecyclerItemClickListener(this, recyclerView, new RecyclerItemClickListener.OnItemClickListener() {//点击事件响应@Overridepublic void onItemClick(View view, int position) {if(!isDeleteState&&diaryList.size()>0){// 普通点击事件diary mDiary=diaryList.get(position);String diaryContent=mDiary.getContent().toString();String diaryTime=mDiary.getTime().toString();Intent intent=new Intent(MainActivity.this,EditActivity.class);//传递内容intent.putExtra("diaryContent",diaryContent);//传入修改标志1:表示修改原有日记内容intent.putExtra("signal",1);startActivity(intent);}else if (diaryList.size()>0){//长按进入多选状态后的点击事件CheckBox checkBox = (CheckBox) view.findViewById(R.id.check_box);if (checkBox.isChecked()) {checkBox.setChecked(false);str.remove(position);} else {str.put(position,diaryAdapter.mDiaryList.get(position).getContent());checkBox.setChecked(true);}}}@Overridepublic void onItemLongClick(View view, int position) {// 长按事件if(!isDeleteState&&diaryList.size()>0){isDeleteState=true;str.clear();//把当前选中的的复选框设置为选中状态diaryAdapter.isSelected.put(position,true);//把所有的CheckBox显示出来RecyclerView recyclerView=(RecyclerView)findViewById(R.id.recycler_view);//第二个参数为true表示长按进入多选删除状态时的适配器初始化adapter=new diaryAdapter(diaryList,true);recyclerView.setAdapter(adapter);str.put(position,diaryAdapter.mDiaryList.get(position).getContent());}}}));}//用户返回该活动时@Overrideprotected void onStart() {super.onStart();isDeleteState=false;if(update==1){diaryList.clear();RecyclerView recyclerView=(RecyclerView)findViewById(R.id.recycler_view);List<diary> data=DataSupport.findAll(diary.class);for(diary mdiary:data){diaryList.add(mdiary);}if(diaryList.size()>0){recyclerView.setLayoutManager(gridLayoutManager);}else{recyclerView.setLayoutManager(linearLayoutManager);}adapter=new diaryAdapter(diaryList,false);recyclerView.setAdapter(adapter);}}@Overrideprotected void onStop() {super.onStop();update=1;//更新数据源}//执行删除的函数private void deleteSelections() {AlertDialog.Builder builder = new AlertDialog.Builder(this);if (str.size()==0) {builder.setTitle("提示").setMessage("当前未选中项目").setPositiveButton("确认", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {RecyclerView recyclerView=(RecyclerView)findViewById(R.id.recycler_view);adapter=new diaryAdapter(diaryList,false);recyclerView.setAdapter(adapter);isDeleteState=false;str.clear();}}).create().show();} else {builder.setTitle("提示");builder.setMessage("确认删除所选日记?");builder.setPositiveButton("确认", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {for(int i:str.keySet()){DataSupport.deleteAll(diary.class,"content=?",str.get(i));}diaryList.clear();List<diary> data=DataSupport.findAll(diary.class);for(diary mdiary:data){diaryList.add(mdiary);}adapter.notifyDataSetChanged();RecyclerView recyclerView=(RecyclerView)findViewById(R.id.recycler_view);if(diaryList.size()>0){recyclerView.setLayoutManager(gridLayoutManager);}else{recyclerView.setLayoutManager(linearLayoutManager);}adapter=new diaryAdapter(diaryList,false);recyclerView.setAdapter(adapter);isDeleteState=false;str.clear();Toast.makeText(MainActivity.this,"删除成功!",Toast.LENGTH_SHORT).show();}});builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {RecyclerView recyclerView=(RecyclerView)findViewById(R.id.recycler_view);adapter=new diaryAdapter(diaryList,false);recyclerView.setAdapter(adapter);isDeleteState=false;str.clear();}});builder.create().show();}}}

diaryAdapter(适配器):

package android.mainactivity;import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v7.widget.CardView;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.TextView;import com.example.a15711.diarypractice.R;import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;/*** Created by 15711 on 2018/10/16.*/public class diaryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {private Boolean isDuoXuan;//是否多选的标志private Context mContext;//上下文public static List<diary> mDiaryList=new ArrayList<>();//数据项列//键为RecyclerView中各子项的position,值为该位置复选框的选中状态public static HashMap<Integer,Boolean> isSelected=new HashMap<>();//这是有日记展示的Holderstatic class ViewHolder extends RecyclerView.ViewHolder{CardView cardView;TextView diaryContent;TextView diaryTime;CheckBox checkBox;public ViewHolder(View view){super(view);cardView=(CardView)view;diaryContent=(TextView)view.findViewById(R.id.diary_content);diaryTime=(TextView)view.findViewById(R.id.diary_time);checkBox=(CheckBox)view.findViewById(R.id.check_box);}}//这是无日记展示的Holderstatic class EmptyViewHolder extends RecyclerView.ViewHolder{View empty_view;TextView textView;public EmptyViewHolder(View view){super(view);empty_view=view;textView=(TextView)view.findViewById(R.id.empty_text);}}//适配器的构造函数public diaryAdapter(List<diary> diaryList,boolean isDuoXuan){mDiaryList=diaryList;this.isDuoXuan=isDuoXuan;}//数据源是否为空,为空返回-1@Overridepublic int getItemViewType(int position) {if(mDiaryList.size()<=0){return -1;}return super.getItemViewType(position);}//创建ViewHolder并返回@NonNull@Overridepublic RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {if(mContext==null){mContext=parent.getContext();}//数据源为空时返回空的子项布局if(viewType==-1){View view=LayoutInflater.from(mContext).inflate(R.layout.diary_empty_item,parent,false);return new EmptyViewHolder(view);}//数据源不为空时返回卡片布局View view= LayoutInflater.from(mContext).inflate(R.layout.diary_item,parent,false);return new ViewHolder(view);}//具体的处理逻辑@Overridepublic void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {//holder为卡片布局的holderif (holder instanceof ViewHolder) {if(isDuoXuan){((ViewHolder) holder).checkBox.setVisibility(View.VISIBLE);//当isSelected中有该位置CheckBox的显示状态时就加载,没有就设为falseif(isSelected.containsKey(position)){((ViewHolder) holder).checkBox.setChecked(isSelected.get(position));}else{isSelected.put(position,false);}}else{//单选状态时清空isSelected,并设置所有复选框不可见isSelected.clear();((ViewHolder) holder).checkBox.setVisibility(View.GONE);}diary mDiary=mDiaryList.get(position);((ViewHolder) holder).diaryContent.setText(mDiary.getContent());((ViewHolder) holder).diaryTime.setText(mDiary.getTime());}}//告诉适配器有多少个项,以便留出足够的空间@Overridepublic int getItemCount() {return mDiaryList.size()>0?mDiaryList.size():1;}
}

RecyclerItemClickListener(RecyclerView的监听器类):

package android.mainactivity;import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;//为RecyclerView设置监听事件public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListener {public interface OnItemClickListener {void onItemClick(View view, int position);void onItemLongClick(View view, int position);}private OnItemClickListener mListener;private GestureDetector mGestureDetector;public RecyclerItemClickListener(Context context, final RecyclerView recyclerView, OnItemClickListener listener) {mListener = listener;mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {@Overridepublic boolean onSingleTapUp(MotionEvent e) {return true;}@Overridepublic void onLongPress(MotionEvent e) {View childView = recyclerView.findChildViewUnder(e.getX(), e.getY());if (childView != null && mListener != null) {mListener.onItemLongClick(childView, recyclerView.getChildAdapterPosition(childView));}}});}@Overridepublic boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) {View childView = view.findChildViewUnder(e.getX(), e.getY());if (childView != null && mListener != null && mGestureDetector.onTouchEvent(e)) {mListener.onItemClick(childView, view.getChildAdapterPosition(childView));}return false;}@Overridepublic void onTouchEvent(RecyclerView view, MotionEvent motionEvent) {}@Overridepublic void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {}
}

这个活动的实现要配合一个RecyclerView适配器和一个RecyclerView监听器,以对点击事件及长按事件进行监听和处理,相对来说比较复杂,但耐心看的话还是很容易理解的,对照注释相信大家都可以看懂,在这里我就不多说了

EditActivity(编辑界面):

package android.editactivity;import android.app.AlertDialog;
import android.content.ContentValues;
import android.content.DialogInterface;
import android.content.Intent;
import android.mainactivity.KeyboardUtils;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.mainactivity.diary;
import android.view.View;
import android.widget.EditText;
import com.example.a15711.diarypractice.R;
import org.litepal.crud.DataSupport;
import java.text.SimpleDateFormat;public class EditActivity extends AppCompatActivity {//接收上个活动传入的日记内容private String diaryContent;//接收上个活动传入的标志private int signal=0;//加载菜单public boolean onCreateOptionsMenu(Menu menu){getMenuInflater().inflate(R.menu.edit_toolbar,menu);return true;}//菜单项的点击事件@Overridepublic boolean onOptionsItemSelected(MenuItem item) {switch (item.getItemId()) {//点击保存case R.id.save_button: {EditText editText=(EditText)findViewById(R.id.edit_content);String content=editText.getText().toString();SimpleDateFormat sdf=new SimpleDateFormat("yyyy年MM月dd日");String time=sdf.format(new java.util.Date());diary mDiary=new diary(content,time);//点击’新建‘后编辑的内容就存储if(signal==0) {mDiary.save();//防止连续点击’存储‘按钮连续存储一样的内容signal=3;}//更新原有内容的就只更新else{//防止连续点击’存储‘按钮连续存储一样的内容signal=3;ContentValues values = new ContentValues();values.put("time", mDiary.getTime().toString());DataSupport.updateAll(diary.class,values,"content=?",diaryContent);values.put("content", mDiary.getContent().toString());DataSupport.updateAll(diary.class,values,"content=?",diaryContent);}//隐藏光标并收起键盘editText.setCursorVisible(false);KeyboardUtils.hideKeyboard(this);break;}//点击返回case android.R.id.home:{//已经保存的直接返回if(signal==3){finish();}//未保存的提示是否保存else{AlertDialog.Builder builder = new AlertDialog.Builder(this);builder.setTitle("");builder.setMessage("保存此次修改吗?");builder.setPositiveButton("确认", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {EditText editText=(EditText)findViewById(R.id.edit_content);String content=editText.getText().toString();SimpleDateFormat sdf=new SimpleDateFormat("yyyy年MM月dd日");String time=sdf.format(new java.util.Date());diary mDiary=new diary(content,time);if(signal==0){mDiary.save();}else{ContentValues values = new ContentValues();values.put("time", mDiary.getTime().toString());DataSupport.updateAll(diary.class,values,"content=?",diaryContent);values.put("content", mDiary.getContent().toString());DataSupport.updateAll(diary.class,values,"content=?",diaryContent);}finish();}});builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {finish();}});builder.create().show();}break;}default:}return true;}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_edit);//接收由MainActivity传来的日记信息Intent intent=getIntent();diaryContent=intent.getStringExtra("diaryContent");signal=intent.getIntExtra("signal",0);Toolbar toolbar=(Toolbar)findViewById(R.id.edit_toolbar);toolbar.setTitle("");setSupportActionBar(toolbar);ActionBar actionBar=getSupportActionBar();if(actionBar!=null){actionBar.setDisplayHomeAsUpEnabled(true);actionBar.setHomeAsUpIndicator(R.drawable.ic_back);}final EditText editText=(EditText)findViewById(R.id.edit_content);editText.setText(diaryContent);//光标放文本后面editText.setSelection(editText.getText().length());editText.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {editText.setCursorVisible(true);}});}
}

这个活动主要实现了保存返回两个按钮的功能,结合注释和之前两个活动的介绍,相信你也能容易理解的


好了,内容也都介绍的差不多了,下面来总结一下这次开发的经验和教训吧。

讲真的在开发过程中,有好几次遇到不能解决的bug时,差点就放弃了,甚至会怀疑是不是代码诚心跟你作对,最后结果证明还是自己在跟自己作对,很多时候只是一些小的细节没有注意到,就可能导致很多匪夷所思的错误,所以思维一定要严谨!不能想当然,不然真的会吃很多苦头。

开发程序的时候一定要有思路,不能想到哪写到哪,那样的话很没有效率而且写出来的代码很乱,在写代码的过程中尽量做到多写注释,这样当你有问题回过头来找错误时会有很大帮助,不然的话你会无从找起,从而浪费更多时间和精力。

开发的过程的确很苦,但只要坚持还是可以成功的,当你的程序成功的跑起来的时候,相信你的内心一定是充满喜悦与满足的。

通过这次的练习我也发现了自己很多不足,包括调试代码找问题的能力,以及深入理解问题并想出解决办法的能力等等,这些都需要自己去提高。


谢谢浏览!
源代码下载

Anroid Studio开发---日记本App相关推荐

  1. Android Studio 开发–微信APP门户界面设计

    Android Studio 开发–微信APP门户界面设计 本次Github代码仓库 --crcr1013/MyWechat 文章目录 Android Studio 开发--微信APP门户界面设计 前 ...

  2. android studio1.2.6,1.2.2 使用Android Studio开发Android APP | 菜鸟教程

    写在前面本节将介绍如何使用Android Studio开发Android APP,和前面Eclipse + ADT + SDK搭建Android开发环境一样,本节也只是介绍一些基本东西,深入的,比如快 ...

  3. 安卓大作业:使用Android Studio开发天气预报APP(使用sqlite数据库)

    使用Android Studio开发天气预报APP 今天我来分享一下如何使用Android Studio开发一个天气预报APP.在文中,我们将使用第三方接口获取实时天气数据,并显示在APP界面上. 步 ...

  4. Android Studio开发安卓app 封装WEB(H5,URL)

    由于项目需要,需使用安卓app封装WEB(H5)url链接,之前用的HbuilerX混合开发,但是Hhuildex的UNIAPP在线打包无法实现开机自动启动功能.需使用离线打包,就干脆学习使用andr ...

  5. Android Studio开发安卓app 安卓与webview中的js交互

    Android Studio开发安卓app 安卓与webview中的js交互 1.webview中的页面调用android中的方法 在Android Studio开发安卓app 封装WEB(H5,UR ...

  6. android studio 开发android app 真机调试

    大家都知道开发android app 的时候可以有2种调试方式, 一种是Android Virtual Device(虚拟模拟器) ,另一种就是真机调试. 这里要说的是真机调试的一些安装步骤: 1. ...

  7. 用Android Studio开发Java App (Runnable Jar)

    ## 步骤一:新建工程及module 若已有Android Studio工程,可在现有工程的基础上来建一个module来完成java代码的编写. 若无工程,我们可先自行新建一个Android工程;再新 ...

  8. Android Studio 开发–微信APP门户界面设计(二)

    本次Github代码仓库 --crcr1013/MyWechat 文章目录 一.成果要求 二.关键步骤 1.准备工作 1.1环境准备 1.2布局构想及资源准备 2. 朋友圈的RecyclerView布 ...

  9. Android Studio开发安卓app TTS学习,使用TextToSpeech类完成输入文字播放中文

    安卓TextToSpeech Android允许您将文本转换为语音.您不仅可以转换它,还可以用多种不同的语言说文本.Android 为此提供了TextToSpeech类.为了使用此类,您需要实例化此类 ...

  10. Android入门教程五之使用AndroidStudio+SDK开发安卓APP

    <!-- Android 基础入门教程 end --><div class="col middle-column big-middle-column">&l ...

最新文章

  1. 虚拟dom_通过编写简易虚拟DOM,来学习虚拟DOM 的原理
  2. 【赠书】深入浅出embedding:原理解析与应用实践
  3. 0xc000007b应用程序无法正常启动_应用程序无法正常启动0xc0000142
  4. android 成长日记 8.Fragment学习之方法回调
  5. 通过Blazor使用C#开发SPA单页面应用程序(2)
  6. linux find 权限不够,超级用户find: `/home/pipi/.gvfs': 权限不够
  7. which oracle linux,(总结)Linux下Oracle11gR2的ORA-00845错误解决方法
  8. 拼接符 防注入正则校验_Apache Kylin 命令注入漏洞调试分析(CVE-2020-1956)
  9. 怎样利用VNC远程连接LINUX桌面
  10. 一加WarpTen技术今日发布:全新旗舰一加7将搭载
  11. python 字符串replace函数_01-Python里字符串的常用操作方法--replace()函数
  12. 人究竟能抠到什么程度?
  13. java保存视频到本地
  14. 解决Win10 /Win11 Fastboot驱动问题
  15. 搞懂激活函数(Sigmoid/ReLU/LeakyReLU/PReLU/ELU)
  16. 学习方法-北大学霸(01)学习方法基础
  17. 揭露培训机构以招聘名义变相招生的欺诈套路!【附上企业黑名单】
  18. 市面上主流RTC竞品对比分析
  19. 卫龙要上市,辣条就不是垃圾食品了?
  20. 你必须认识的五名网络女红人

热门文章

  1. 数据可视化之美-动态图绘制【补充】(以Python为工具)
  2. 缓解疲劳、舒缓全身放松,游养乐分享养生小秘籍
  3. Win10强制更新关闭方法
  4. Android 高级开发进阶图谱
  5. 如何利用Spring Boot框架开发一个全球化的应用程序
  6. 用Python写一个简单的24点计算器
  7. 如何选择家庭私有云NAS方案?家庭NAS存储服务器的重要性
  8. 【python+selenium】自动登陆学校青果教务网
  9. 蓝桥杯 java 跳马问题
  10. 关于“质量”概念的理解