使用说明
系统
自定义了APP的图标
-
启动app查看到启动动画(图片)
P.S. 可能会出现启动动画消失回到桌面的情况,等待后进入app
进入app默认显示主界面,底部标签为英雄、装备、铭文、技能
进入app后播放背景音乐
英雄主界面
- 默认显示:搜索框,工具框(
ToolBar
),添加英雄按钮,收藏英雄显示,英雄分类显示还有具体的英雄列表 - 点击搜索框出现候选英雄列表(不输入会默认显示所有英雄列表,输入文字后会匹配输入文本为前缀的英雄名),点击搜索按钮或者候选框中的英雄名称进入英雄详情,如果搜索框中的英雄名不存在会弹出
Toast
提示 - 这是一个图片轮换器,理论上会3秒切换一个英雄(显示英雄海报),且支持循环切换(最后一个英雄切换回最前面一个),在英雄详情页面会有收藏与取消收藏的操作,下面的小圆点会指示当前图片的位置(红色指示)
- 添加英雄的按钮,点击之后会跳到添加英雄的页面
- 英雄职业的分类显示,点击不同的按钮就会过滤出该职业的英雄,更新英雄列表(搜索框提示列表不受影响)
- 英雄列表,网格布局,一共五列,只显示英雄头像,点击会进入英雄详情
英雄详情
- 返回主界面按钮
- 当前页面名称(英雄)
- 更多操作(编辑,删除,保存)
- 点击编辑之后,所有可修改的内容都处于可编辑状态(默认是不可编辑状态)
- 点击保存之后才能保存内容的修改(包括收藏!收藏之后直接返回是无效的,需要点击保存)
- 点击删除按钮之后弹出确认对话框,确定之后删除该英雄并且返回主界面
- 英雄头像(可修改)
- 英雄名字(不可修改)
- 英雄称号(可修改)
- 英雄职业(可修改)
- 英雄海报(可修改)
- 收藏英雄或者取消收藏(只能点击,不能修改)
- 英雄生存能力,攻击伤害,技能效果,上手难度数值显示(可修改)
- 英雄技能图标按钮,点击查看不同的技能(包括被动只有四个技能,天美最近的英雄技能越来越多了……)(不可修改)
- 英雄技能具体描述(不可修改)
- 英雄推荐出装(不可修改),点击会跳转到相应的装备详情页面
添加英雄
- 返回主界面
- 更多操作
- 添加头像,弹出系统图片选择框选择图片作为英雄头像(默认是:王者荣耀图标)
- 添加语音,弹出系统语音选择框作为英雄语音(默认是:PentaKill语音,享受五杀的感觉!)
- 选择英雄海报(默认是:当前图片)
- 填写英雄名字(不可为空,不可重复)
- 填写英雄称号(默认为:王者小兵)
- 填写英雄职业(默认为:法师)
- 填写英雄生存能力,攻击伤害,技能效果,上手难度等信息(范围是1-9,默认是1)
导航栏(SmartTabLayout)
使用了GitHub上的这个UI
https://github.com/ogaclejapan/SmartTabLayout
所以activity_main.xml
中只有一个ViewPager
和一个导航栏,(其实导航栏是在上面)
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
/>
<com.ogaclejapan.smarttablayout.SmartTabLayout
android:background="@color/colorPrimaryDark"
android:id="@+id/viewpagertab"
android:layout_width="match_parent"
android:layout_height="50dp"
app:stl_indicatorAlwaysInCenter="false"
app:stl_indicatorWithoutPadding="false"
app:stl_indicatorInFront="false"
app:stl_indicatorInterpolation="smart"
app:stl_indicatorGravity="bottom"
app:stl_indicatorColor="#40C4FF"
app:stl_indicatorThickness="4dp"
app:stl_indicatorWidth="auto"
app:stl_indicatorCornerRadius="2dp"
app:stl_overlineColor="@color/colorPrimary"
app:stl_overlineThickness="0dp"
app:stl_underlineColor="#4D000000"
app:stl_underlineThickness="1dp"
app:stl_dividerColor="#4D000000"
app:stl_dividerThickness="1dp"
app:stl_defaultTabBackground="?attr/selectableItemBackground"
app:stl_defaultTabTextAllCaps="true"
app:stl_defaultTabTextColor="@color/white"
app:stl_defaultTabTextSize="12sp"
app:stl_defaultTabTextHorizontalPadding="16dp"
app:stl_defaultTabTextMinWidth="0dp"
app:stl_distributeEvenly="true"
app:stl_clickable="true"
app:stl_titleOffset="24dp"
app:stl_drawDecorationAfterTab="false"
/>
MainActivity.java
FragmentPagerItemAdapter adapter = new FragmentPagerItemAdapter(
getSupportFragmentManager(), FragmentPagerItems.with(this)
.add(R.string.title1, Fragment1.class)
.add(R.string.title2, Fragment2.class)
.add(R.string.title3, Fragment3.class)
.add(R.string.title4, Fragment4.class)
.create());
ViewPager viewPager = findViewById(R.id.viewpager);
viewPager.setAdapter(adapter);
SmartTabLayout viewPagerTab = findViewById(R.id.viewpagertab);
viewPagerTab.setViewPager(viewPager);
这里的Fragment1,2,3,4
分别是四个页面:英雄,装备,明文,技能
工具栏(ToolBar)
其实有两种ToolBar
:
android.support.v7.widget.Toolbar
Toolbar
两个都能用,但是有一些区别
区别一:ToolBar
的就是MenuItem
显示不出来图标,另一个可以
区别二:android.support.v7.widget.Toolbar
在设计页面没有预览,另一个有
区别三:android.support.v7.widget.Toolbar
如果是来自其他Activity
跳转过来的,它会自动加上标题,而且会自动有返回按钮,而且添加的菜单栏不太好设计(具体我也忘了),所以除了主界面,其他界面我都是用Toolbar
,方便自己定制
使用方法:
fragment1.xml
<android.support.v7.widget.Toolbar
android:id="@+id/hero_toolbar"
android:layout_width="match_parent"
android:layout_height="45dp"
android:background="@color/colorPrimaryDark">
</android.support.v7.widget.Toolbar>
在Android
视图下创建一个新的文件夹menu
新建hero_menu.xml
,
用来保存ToolBar
上的菜单栏布局
这里只有一个菜单,表示添加英雄,而且图标用定义的图标
showAsAction
表示是否展开到工具栏上
-
always
表示总是展开 -
ifRoom
表示如果有足够的空间就展开 -
never
表示总是不展开 -
withText
表示使菜单项和它的图标,菜单文本一起显示。
那么不展开的菜单会到哪里呢?
比如英雄详情页面,它会隐藏在更多按钮里(显示为三个点)
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_hero_add"
android:title="添加英雄"
android:icon="@mipmap/add"
app:showAsAction="always" />
</menu>
然后需要在Fragment1.java
文件中动态设置ToolBar
的样式,以及设置点击Item
响应的事件,这里只有一个事件,就是跳转到添加英雄页面
//获取ToolBar控件
android.support.v7.widget.Toolbar toolbar = view.findViewById(R.id.hero_toolbar);
toolbar.inflateMenu(R.menu.hero_menu);//设置右上角的填充菜单
toolbar.setOnMenuItemClickListener(new android.support.v7.widget.Toolbar.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem menuItem) {
switch (menuItem.getItemId()) {
//添加英雄
case R.id.action_hero_add:
Intent intent = new Intent(getActivity(), HeroAdd.class);
intent.putStringArrayListExtra("hero_names", mAdapter.getAllNames());
startActivityForResult(intent, 1);
break;
}
return true;
}
});
还有其他的菜单布局,待会会使用
hero_detail_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_hero_edit"
android:title="编辑"
android:icon="@mipmap/edit"
app:showAsAction="never" />
<item
android:id="@+id/action_hero_save"
android:title="保存"
android:icon="@mipmap/save"
app:showAsAction="never" />
<item
android:id="@+id/action_hero_delete"
android:title="删除"
android:icon="@mipmap/delete"
app:showAsAction="never"/>
</menu>
hero_add_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_hero_add_icon"
android:title="添加头像"
app:showAsAction="never"/>
<item
android:id="@+id/action_hero_add_voice"
android:title="添加语音"
app:showAsAction="never"/>
<item
android:id="@+id/action_hero_add_save"
android:title="保存英雄"
android:icon="@mipmap/white_save"
app:showAsAction="ifRoom" />
</menu>
java代码待会再讨论
英雄列表(RecyclerView)
英雄的实体类Hero.java
public class Hero implements Serializable{
//保存的是image的uri
//英雄海报
private String image = "android.resource://com.team1.kingofhonor/" +R.mipmap.hero;
//英雄语音
private String voice = "android.resource://com.team1.kingofhonor/" +R.raw.pentakill;
//英雄图标
private String icon = "android.resource://com.team1.kingofhonor/" +R.mipmap.hero_icon;
//英雄技能图标
private String skill1_icon = "android.resource://com.team1.kingofhonor/" +R.mipmap.juyoujing1;
private String skill2_icon = "android.resource://com.team1.kingofhonor/" +R.mipmap.juyoujing2;
private String skill3_icon = "android.resource://com.team1.kingofhonor/" +R.mipmap.juyoujing3;
private String skill4_icon = "android.resource://com.team1.kingofhonor/" +R.mipmap.juyoujing4;
//英雄技能描述
private String skill1_description = "技能1";
private String skill2_description = "技能2";
private String skill3_description = "技能3";
private String skill4_description = "技能4";
//英雄名字
private String name = "名字未设置";
//英雄称号
private String alias = "王者小兵";
//英雄职业
private String category;//1.法师 2.刺客 3.射手 4.辅助 5.战士 6.坦克
//推荐装备
private String equip1 = "破军";
private String equip2 = "破军";
private String equip3 = "破军";
private String equip4 = "破军";
private String equip5 = "破军";
private String equip6 = "破军";
//生存能力
private int viability = 1;
//攻击伤害
private int attack_damage= 1;
//技能伤害
private int skill_damage = 1;
//上手难度
private int difficulty = 1;
private Boolean favorite = false;//收藏
private Boolean deleted = false;//true表示已经删除
private Boolean added = false;//true表示是新加的
private Boolean modified = false;//true表示已经修改
public Hero(){}
...
}
这里英雄的语音,头像,海报什么的资源文件全都是存储对应的Uri字符串
列表布局hero_list.xml
其实就显示了一个英雄头像
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_margin="5dp"
xmlns:app="http://schemas.android.com/apk/res-auto">
<ImageView
android:id="@+id/hero_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
android:src="@mipmap/juyoujing_icon"/>
</android.support.constraint.ConstraintLayout>
页面视图fragment1.xml
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/hero_recyclerview"
tools:listitem="@layout/hero_list"
android:dividerHeight="10dp"
android:layout_marginTop="290dp"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
</android.support.v7.widget.RecyclerView>
列表适配器HeroAdapter.java
这里仅仅实现了基本的显示功能,后面还会添加新的方法
package com.team1.kingofhonor.adapter;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.team1.kingofhonor.R;
import com.team1.kingofhonor.model.Hero;
import com.team1.kingofhonor.sqlite.HeroSQLiteHelper;
import java.util.ArrayList;
import java.util.List;
public class HeroAdapter extends RecyclerView.Adapter<HeroAdapter.ViewHolder> {
private static List<Hero> mDatas;
private HeroAdapter.OnItemClickListener onItemClickListener;
public HeroAdapter(List<Hero> list) {
mDatas = list;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
// 实例化展示的view
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.hero_list, parent, false);
// 实例化viewholder
ViewHolder viewHolder = new ViewHolder(v);
return viewHolder;
}
@Override
public void onBindViewHolder(final ViewHolder holder, int position) {
// 绑定数据
//holder.hero_name.setText(mDatas.get(position).getName());
holder.hero_image.setImageURI(Uri.parse(mDatas.get(position).getIcon()));
//listener
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(final View v) {
if(onItemClickListener != null) {
int pos = holder.getLayoutPosition();
onItemClickListener.onItemClick(holder.itemView, pos);
}
}
});
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
if(onItemClickListener != null) {
int pos = holder.getLayoutPosition();
onItemClickListener.onItemLongClick(holder.itemView, pos);
}
//表示此事件已经消费,不会触发单击事件
return true;
}
});
}
@Override
public int getItemCount()
{
return mDatas.size();
}
public void addNewItem(Hero f) {
if(mDatas == null) {
mDatas = new ArrayList<>();
}
mDatas.add(0, f);
notifyItemInserted(0);
}
public void deleteItem(int pos) {
if(mDatas == null || mDatas.isEmpty()) {
return;
}
mDatas.remove(pos);
notifyItemRemoved(pos);
}
public Hero getItem(int pos){
if(mDatas == null || mDatas.isEmpty())
return null;
return mDatas.get(pos);
}
/**
* 设置回调监听
*
* @param listener
*/
public void setOnItemClickListener(HeroAdapter.OnItemClickListener listener) {
this.onItemClickListener = listener;
}
public interface OnItemClickListener {
void onItemClick(View view, int position);
void onItemLongClick(View view, int position);
}
public static class ViewHolder extends RecyclerView.ViewHolder {
TextView hero_name;
ImageView hero_image;
public ViewHolder(View itemView) {
super(itemView);
//hero_name = itemView.findViewById(R.id.hero_name);
hero_image = itemView.findViewById(R.id.hero_image);
}
}
}
代码文件Fragment1.java
这里设置成网格显示,一共有5列
private View view;
private RecyclerView.LayoutManager mLayoutManager;
private RecyclerView mRecyclerView;
private HeroAdapter mAdapter;
...
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
view = LayoutInflater.from(getActivity()).inflate(R.layout.fragment1, container, false);
//hero list
mLayoutManager = new GridLayoutManager(getActivity(), 5);
mAdapter = new HeroAdapter(mySQLiteHelper.getAllHeroes());
mRecyclerView = view.findViewById(R.id.hero_recyclerview);
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setAdapter(mAdapter);
//设置英雄列表的边距
//mRecyclerView.addItemDecoration(new SpaceItemDecoration(40));
mAdapter.setOnItemClickListener(new HeroAdapter.OnItemClickListener() {
@Override
public void onItemClick(View view, int position) {
Intent intent = new Intent(getActivity(), HeroDetail.class);
Bundle bundle=new Bundle();
bundle.putSerializable("Click_Hero", mAdapter.getItem(position));
intent.putExtras(bundle);
clickPosition = position;
startActivityForResult(intent, 0);
}
@Override
public void onItemLongClick(View view, int position) {
}
});
}
英雄数据库(HeroSQLiteHelper)
HeroSQLiteHelper.java
除了实现基本的功能外,主要是加了个getAllHeroes
方法,这是为了最开始得到数据库的数据给HeroAdapter
赋初值
public class HeroSQLiteHelper extends SQLiteOpenHelper {
// Database Version
private static final int DATABASE_VERSION = 2;
// Database Name
private static final String DATABASE_NAME = "HeroDB";
public HeroSQLiteHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
// SQL statement to create book table
String CREATE_HERO_TABLE = "CREATE TABLE heroes ( " +
"name TEXT PRIMARY KEY, " +
"image TEXT," +
"alias TEXT," +
"category TEXT," +
"viability INTEGER,"+
"attack_damage INTEGER,"+
"skill_damage INTEGER,"+
"difficulty INTEGER,"+
"voice TEXT," +
"icon TEXT," +
"favorite INTEGER," +
"skill1_icon TEXT," +
"skill1_description TEXT," +
"skill2_icon TEXT," +
"skill2_description TEXT," +
"skill3_icon TEXT," +
"skill3_description TEXT," +
"skill4_icon TEXT," +
"skill4_description TEXT)";
// create books table
db.execSQL(CREATE_HERO_TABLE);
db.execSQL(CREATE_EQUIP_TABLE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// Drop older table if existed
db.execSQL("DROP TABLE IF EXISTS heroes");
this.onCreate(db);
}
//执行查询语句
public Cursor query(String sql, String[] args)
{
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.rawQuery(sql, args);
return cursor;
}
// table name
private static final String TABLE_HEROES = "heroes";
//Table Columns names
private static final String KEY_NAME = "name";
private static final String KEY_ALIAS = "alias";
private static final String KEY_IMAGE = "image";
private static final String KEY_CATEGORY = "CATEGORY";
private static final String KEY_VIABILITY = "viability";
private static final String KEY_ATTACK_DAMAGE = "attack_damage";
private static final String KEY_SKILL_DAMAGE = "skill_damage";
private static final String KEY_DIFFICULTY = "difficulty";
private static final String KEY_VOICE = "voice";
private static final String KEY_ICON = "icon";
private static final String KEY_FAVORITE = "favorite";
private static final String KEY_SKILL1_ICON = "skill1_icon";
private static final String KEY_SKILL2_ICON = "skill2_icon";
private static final String KEY_SKILL3_ICON = "skill3_icon";
private static final String KEY_SKILL4_ICON = "skill4_icon";
private static final String KEY_SKILL1_DESCRIPTION = "skill1_description";
private static final String KEY_SKILL2_DESCRIPTION = "skill2_description";
private static final String KEY_SKILL3_DESCRIPTION = "skill3_description";
private static final String KEY_SKILL4_DESCRIPTION = "skill4_description";
private static final String[] COLUMNS = {KEY_NAME,KEY_ALIAS,KEY_IMAGE,KEY_CATEGORY};
public void addHero(Hero hero){
Log.d("addHero", hero.toString());
// 1. get reference to writable DB
SQLiteDatabase db = this.getWritableDatabase();
// 2. create ContentValues to add key "column"/value
ContentValues values = new ContentValues();
values.put(KEY_NAME, hero.getName());
values.put(KEY_ALIAS, hero.getAlias());
values.put(KEY_IMAGE, hero.getImage());
values.put(KEY_CATEGORY, hero.getCategory());
values.put(KEY_VIABILITY, hero.getViability());
values.put(KEY_ATTACK_DAMAGE, hero.getAttack_damage());
values.put(KEY_SKILL_DAMAGE, hero.getSkill_damage());
values.put(KEY_DIFFICULTY, hero.getDifficulty());
values.put(KEY_VOICE, hero.getVoice());
values.put(KEY_ICON, hero.getIcon());
values.put(KEY_FAVORITE, hero.getFavorite());
values.put(KEY_SKILL1_ICON, hero.getSkill1_icon());
values.put(KEY_SKILL1_DESCRIPTION, hero.getSkill1_description());
values.put(KEY_SKILL2_ICON, hero.getSkill2_icon());
values.put(KEY_SKILL2_DESCRIPTION, hero.getSkill2_description());
values.put(KEY_SKILL3_ICON, hero.getSkill3_icon());
values.put(KEY_SKILL3_DESCRIPTION, hero.getSkill3_description());
values.put(KEY_SKILL4_ICON, hero.getSkill4_icon());
values.put(KEY_SKILL4_DESCRIPTION, hero.getSkill4_description());
// 3. insert
db.insert(TABLE_HEROES, // table
null, //nullColumnHack
values); // key/value -> keys = column names/ values = column values
// 4. close
db.close();
}
public Hero getHero(String name){
// 1. get reference to readable DB
SQLiteDatabase db = this.getReadableDatabase();
// 2. build query
Cursor cursor =
db.query(TABLE_HEROES, // a. table
COLUMNS, // b. column names
" name = ?", // c. selections
new String[] { name }, // d. selections args
null, // e. group by
null, // f. having
null, // g. order by
null); // h. limit
// 3. if we got results get the first one
if (cursor != null)
cursor.moveToFirst();
// 4. build object
Hero hero = new Hero();
hero.setName(cursor.getString(0));
hero.setImage(cursor.getString(1));
hero.setAlias(cursor.getString(2));
hero.setCategory(cursor.getString(3));
hero.setViability(cursor.getInt(4));
hero.setAttack_damage(cursor.getInt(5));
hero.setSkill_damage(cursor.getInt(6));
hero.setDifficulty(cursor.getInt(7));
hero.setVoice(cursor.getString(8));
hero.setIcon(cursor.getString(9));
hero.setFavorite(cursor.getInt(10) == 1);
hero.setSkill1_icon(cursor.getString(11));
hero.setSkill1_description(cursor.getString(12));
hero.setSkill2_icon(cursor.getString(13));
hero.setSkill2_description(cursor.getString(14));
hero.setSkill3_icon(cursor.getString(15));
hero.setSkill3_description(cursor.getString(16));
hero.setSkill4_icon(cursor.getString(17));
hero.setSkill4_description(cursor.getString(18));
// 5. return hero
return hero;
}
// Get All Heroes
public List<Hero> getAllHeroes() {
List<Hero> heroes = new LinkedList<Hero>();
// 1. build the query
String query = "SELECT * FROM " + TABLE_HEROES;
// 2. get reference to writable DB
SQLiteDatabase db = this.getWritableDatabase();
Cursor cursor = db.rawQuery(query, null);
// 3. go over each row, build book and add it to list
Hero hero = null;
if (cursor.moveToFirst()) {
do {
hero = new Hero();
hero.setName(cursor.getString(0));
hero.setImage(cursor.getString(1));
hero.setAlias(cursor.getString(2));
hero.setCategory(cursor.getString(3));
hero.setViability(cursor.getInt(4));
hero.setAttack_damage(cursor.getInt(5));
hero.setSkill_damage(cursor.getInt(6));
hero.setDifficulty(cursor.getInt(7));
hero.setVoice(cursor.getString(8));
hero.setIcon(cursor.getString(9));
hero.setFavorite(cursor.getInt(10) == 1);
hero.setSkill1_icon(cursor.getString(11));
hero.setSkill1_description(cursor.getString(12));
hero.setSkill2_icon(cursor.getString(13));
hero.setSkill2_description(cursor.getString(14));
hero.setSkill3_icon(cursor.getString(15));
hero.setSkill3_description(cursor.getString(16));
hero.setSkill4_icon(cursor.getString(17));
hero.setSkill4_description(cursor.getString(18));
heroes.add(hero);
} while (cursor.moveToNext());
}
// return heros
return heroes;
}
public void deleteAllHeroes(){
// 1. get reference to writable DB
SQLiteDatabase db = this.getWritableDatabase();
// 2. delete
db.delete(TABLE_HEROES,null,null);
// 3. close
db.close();
}
// Updating single book
public int updateHero(Hero hero) {
// 1. get reference to writable DB
SQLiteDatabase db = this.getWritableDatabase();
// 2. create ContentValues to add key "column"/value
ContentValues values = new ContentValues();
values.put("name", hero.getName());
values.put("image", hero.getImage().toString());
values.put("alias", hero.getAlias());
values.put("category", hero.getCategory());
values.put(KEY_VIABILITY, hero.getViability());
values.put(KEY_ATTACK_DAMAGE, hero.getAttack_damage());
values.put(KEY_SKILL_DAMAGE, hero.getSkill_damage());
values.put(KEY_DIFFICULTY, hero.getDifficulty());
values.put(KEY_VOICE, hero.getVoice());
values.put(KEY_ICON, hero.getIcon());
values.put(KEY_FAVORITE, hero.getFavorite());
values.put(KEY_SKILL1_ICON, hero.getSkill1_icon());
values.put(KEY_SKILL1_DESCRIPTION, hero.getSkill1_description());
values.put(KEY_SKILL2_ICON, hero.getSkill1_icon());
values.put(KEY_SKILL2_DESCRIPTION, hero.getSkill2_description());
values.put(KEY_SKILL3_ICON, hero.getSkill1_icon());
values.put(KEY_SKILL3_DESCRIPTION, hero.getSkill3_description());
values.put(KEY_SKILL4_ICON, hero.getSkill1_icon());
values.put(KEY_SKILL4_DESCRIPTION, hero.getSkill4_description());
// 3. updating row
int i = db.update(TABLE_HEROES, //table
values, // column/value
KEY_NAME+" = ?", // selections
new String[] { hero.getName() }); //selection args
// 4. close
db.close();
return i;
}
// Deleting single hero
public void deleteHero(Hero hero) {
// 1. get reference to writable DB
SQLiteDatabase db = this.getWritableDatabase();
// 2. delete
db.delete(TABLE_HEROES,
KEY_NAME+" = ?",
new String[] { hero.getName() });
// 3. close
db.close();
}
}
这里我设置的主键是英雄名,查找起来可能会方便点吧,因此要求英雄不能重名
增加英雄
静态添加:
在MainActivity.java
中会初始化一些英雄数据,放到数据库里
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//添加数据库
heroSQLiteHelper = new HeroSQLiteHelper(this);
heroSQLiteHelper.deleteAllHeroes();
//下面用来添加数据
heroSQLiteHelper.addHero(new Hero("橘右京", "android.resource://" + getApplicationContext().getPackageName() + "/" +R.mipmap.juyoujing, "神梦一刀", "刺客" , 5, 6, 5 ,5,
"android.resource://com.team1.kingofhonor/" + R.raw.juyoujing,
"android.resource://com.team1.kingofhonor/" + R.mipmap.juyoujing_icon, true,
"android.resource://com.team1.kingofhonor/" + R.mipmap.juyoujing1, "秘剑胧刀 冷却值:5消耗:0\n" +
"\n" +
"被动:橘右京将下一次普攻将进行一次强力拔刀斩,对前方敌人造成130%物理加成物理伤害并减少50%移动速度,持续2秒;拔刀斩每5秒可准备1次;对处于攻击边缘的敌人将承受50%的额外伤害.",
"android.resource://com.team1.kingofhonor/" + R.mipmap.juyoujing2, "燕返 冷却值:7消耗:0\n" +
"\n" +
"橘右京跃向后方,同时向前方挥刀,对附近的敌人造成200/240/280/320/360/400(+145%物理加成)点物理伤害,若成功命中一名敌方英雄,可激活二段使用",
"android.resource://com.team1.kingofhonor/" + R.mipmap.juyoujing3, "居合 冷却值:10/9.5/9/8.5/8/7.5消耗:0\n" +
"\n" +
"橘右京向指定方向快速拔刀,对路径上的第一个敌人造成330/375/420/465/510/555(+212%物理加成)点物理伤害,对路上的其余敌人造成的伤害将衰减50%,并将路径末端的敌人眩晕0.75秒",
"android.resource://com.team1.kingofhonor/" + R.mipmap.juyoujing4, "细雪 冷却值:12/11.5/11消耗:0\n" +
"\n" +
"橘右京向指定方向连续拔刀四次,每次命中造成100/150/200(+70%物理加成)点物理伤害并且每次命中敌方英雄,自身将回复70/100/130(+35%物理加成)点生命值(命中非英雄单位效果减半)"));
...
}
在Fragment1.java
就可以通过数据库中的英雄数据填补Adapter
mySQLiteHelper= new HeroSQLiteHelper(getContext());
mAdapter = new HeroAdapter(mySQLiteHelper.getAllHeroes());
动态添加:
在主界面点击右上角的加号可以进入到一个新的Activity
activity_hero_add.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"
android:orientation="vertical"
android:scrollbars="vertical"
tools:context=".HeroAdd">
<Toolbar
android:id="@+id/hero_add_toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/colorPrimaryDark"
android:navigationIcon="@mipmap/back">
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="添加英雄"
android:gravity="center"
android:textSize="20sp"
android:textColor="@color/white"/>
</Toolbar>
<ScrollView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scrollbars="vertical">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageButton
android:id="@+id/hero_add_image"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="0dp"
android:scaleType="fitXY"
android:adjustViewBounds="true"
android:src="@mipmap/juyoujing" />
<LinearLayout
android:layout_marginStart="10dp"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:orientation="horizontal">
<TextView
android:id="@+id/hero_add_name_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="英雄名称:"
android:textSize="22sp"
android:layout_marginEnd="10dp"
android:gravity="center"/>
<EditText
android:id="@+id/hero_add_name"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:hint="橘右京"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="2dp"
android:background="@color/colorPrimary"/>
<LinearLayout
android:layout_marginStart="10dp"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:orientation="horizontal">
<TextView
android:id="@+id/hero_add_alias_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:textSize="22sp"
android:layout_marginEnd="10dp"
android:text="英雄称号:" />
<EditText
android:id="@+id/hero_add_alias"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:hint="神梦一刀" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="2dp"
android:background="@color/colorPrimary"/>
<LinearLayout
android:layout_marginStart="10dp"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:orientation="horizontal">
<TextView
android:id="@+id/hero_add_category_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:textSize="22sp"
android:layout_marginEnd="10dp"
android:text="英雄职业:" />
<Spinner
android:id="@+id/hero_add_category"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:entries="@array/herocategory_add"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="2dp"
android:background="@color/colorPrimary"/>
<LinearLayout
android:layout_marginStart="10dp"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:layout_marginTop="5dp"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="生存能力:"
android:textSize="22sp"
android:layout_marginEnd="10dp"
/>
<org.adw.library.widgets.discreteseekbar.DiscreteSeekBar
android:id="@+id/hero_add_viability"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:dsb_min="1"
app:dsb_max="9"
app:dsb_progressColor="@color/lightblue"
android:layout_marginEnd="10dp"
/>
</LinearLayout>
<LinearLayout
android:layout_marginStart="10dp"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="攻击伤害:"
android:textSize="22sp"
android:layout_marginEnd="10dp"
/>
<org.adw.library.widgets.discreteseekbar.DiscreteSeekBar
android:id="@+id/hero_add_attack_damage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:dsb_min="1"
app:dsb_max="9"
app:dsb_progressColor="@color/red"
android:layout_marginEnd="10dp"
/>
</LinearLayout>
<LinearLayout
android:layout_marginStart="10dp"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="技能伤害:"
android:textSize="22sp"
android:layout_marginEnd="10dp"
/>
<org.adw.library.widgets.discreteseekbar.DiscreteSeekBar
android:id="@+id/hero_add_skill_damage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:dsb_min="1"
app:dsb_max="9"
app:dsb_progressColor="@color/purple"
android:layout_marginEnd="10dp"
/>
</LinearLayout>
<LinearLayout
android:layout_marginStart="10dp"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="上手难度:"
android:textSize="22sp"
android:layout_marginEnd="10dp"
/>
<org.adw.library.widgets.discreteseekbar.DiscreteSeekBar
android:id="@+id/hero_add_difficulty"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:dsb_min="1"
app:dsb_max="9"
app:dsb_progressColor="@color/chocolate"
android:layout_marginEnd="10dp"
/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="2dp"
android:background="@color/colorPrimary"/>
</LinearLayout>
</ScrollView>
</LinearLayout>
HeroAdd.java
能添加的属性有英雄名称,英雄称号,英雄职业,英雄头像,英雄海报,英雄语音,英雄生存能力、攻击伤害、技能伤害、上手难度(没有添加技能)
其实大部分属性都可以不填就能添加成功,只有英雄名称一定要填写(因为且不止因为它是数据库表的主键),然后就有一个判断重名的问题,这是的做法是从Fagment1
的ToolBar
跳转过来的时候传入一个保存所有英雄名的数组,添加的时候如果新加的英雄名在这个数组里面就会添加失败!
//比较无重复的英雄名称
hero_names = getIntent().getStringArrayListExtra("hero_names");
当然需要在Adapter
新加一个获取所有英雄名称的方法
//返回所有的英雄数据
public ArrayList<String> getAllNames(){
ArrayList<String> data = new ArrayList<>();
for(Hero temp : mDatas){
data.add(temp.getName());
}
return data;
}
选取图片
有两个地方需要添加图片,一个直接点击屏幕上的海报图片选取添加英雄的图片,一个是右上角的菜单添加英雄的头像
以点击页面上的ImageButton
(中间那个大图片)为例
通过下面的代码调用了系统的文件选择器,当然把文件类型局限在图片
这里的 startActivityForResult
传递的参数是1,是为了跟另外一种选取图片的方式作区别,不然选择的图片不知道该作为英雄的海报还是英雄的头像
imageButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
intent.addCategory(Intent.CATEGORY_OPENABLE);
startActivityForResult(intent,1);
}
});
重写onActivityResult
方法,用来选取图片返回结果的处理
这里我做的处理就是把图片复制一份到该应用的file文件夹里(使用BitmapFactory
),再保存新图片的Uri,其中新图片的名字由UUID生成,如果是海报的话还会更新页面上的图片按钮
不这么做而是直接保存选取图片的Uri会有两个问题
- 如果以后删除了相册了这个图片,那么引用就会有问题
- 更大的问题是其实这次对话结束后,以后再访问这个Uri的图片会出下权限错误,因为这个ACTION_GET_CONTENT权限是一次性的,后来即使知道Uri也不能访问
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(requestCode == 1){
if (resultCode == RESULT_OK) {
Uri uri = data.getData();
ContentResolver cr = this.getContentResolver();
try {
Bitmap bitmap = BitmapFactory.decodeStream(cr.openInputStream(uri));
//通过UUID生成字符串文件名
String image_name = UUID.randomUUID().toString() + ".jpg";
//存储图片
FileOutputStream out = openFileOutput(image_name, MODE_PRIVATE);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
//获取复制后文件的uri
Uri image_file_uri = Uri.fromFile(getFileStreamPath(image_name));
//图片预览
imageButton.setImageURI(image_file_uri);
//保存该URI
image_uri = image_file_uri.toString();
out.flush();
out.close();
} catch (FileNotFoundException e) {
Log.e("FileNotFoundException", e.getMessage(),e);
} catch (IOException e) {
Log.w("IOException", e.getMessage(), e);
}
}
}else if(requestCode == 2){
if (resultCode == RESULT_OK) {
Uri uri = data.getData();
ContentResolver cr = this.getContentResolver();
try {
Bitmap bitmap = BitmapFactory.decodeStream(cr.openInputStream(uri));
//通过UUID生成字符串文件名
String image_name = UUID.randomUUID().toString() + ".jpg";
//存储图片
FileOutputStream out = openFileOutput(image_name, MODE_PRIVATE);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
//获取复制后文件的uri
Uri image_file_uri = Uri.fromFile(getFileStreamPath(image_name));
//保存该URI
icon_uri = image_file_uri.toString();
out.flush();
out.close();
} catch (FileNotFoundException e) {
Log.e("FileNotFoundException", e.getMessage(),e);
} catch (IOException e) {
Log.w("IOException", e.getMessage(), e);
}
}
...
选取语音
在菜单栏里调用系统文件选取器
这里将ResultCode设置为3,跟前面选取图片作区分
//添加语音
case R.id.action_hero_add_voice:
intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("audio/*");
intent.addCategory(Intent.CATEGORY_OPENABLE);
startActivityForResult(intent,3);
break;
对返回的结果做解析,这里同样是复制音频,然后也会预览,预览的办法就是调用MediaPlayer
播放这个音频
这也算遇到的一个挺大的问题吧,图片还可以通过
BitmapFactory
编码解码,但是音频我还不知道怎么编码解码,好像网上很多人都是自己写方法调用,于是想了一个很简单的办法,就是直接把这个文件转换为字节流复制过去(所以这跟前面图片的复制本质上是不一样的)
else if(requestCode == 3){
if (resultCode == RESULT_OK) {
voice_uri = data.getData().toString();
ContentResolver cr = this.getContentResolver();
//播放预览
mediaPlayer = MediaPlayer.create(HeroAdd.this, Uri.parse(voice_uri));
mediaPlayer.start();
try {
//通过UUID生成字符串文件名
String voice_name = UUID.randomUUID().toString() + ".mp3";
//存储音频
FileOutputStream out = openFileOutput(voice_name, MODE_PRIVATE);
InputStream in = cr.openInputStream(data.getData());
byte[] newVoice = new byte[in.available()];
in.read(newVoice);
out.write(newVoice);
in.close();
out.close();
voice_uri = Uri.fromFile(getFileStreamPath(voice_name)).toString();
} catch (FileNotFoundException e) {
Log.e("FileNotFoundException", e.getMessage(),e);
} catch (IOException e) {
Log.w("IOException", e.getMessage(), e);
}
}
播放音频的后续工作就是记得在Activity
销毁的时候记得销毁这个MediaPlayer
@Override
protected void onDestroy() {
super.onDestroy();
if(mediaPlayer != null)
mediaPlayer.release();
}
选择职业
这里使用了一个Spinner
控件
<Spinner
android:id="@+id/hero_add_category"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:entries="@array/herocategory_add"/>
展开它能选择的值定义在values
文件夹中新建一个xml
文件(文件名任意取)
<?xml version="1.0" encoding="utf-8"?>
<resources>
</string-array>
<string-array name="herocategory_add">
<item>法师</item>
<item>刺客</item>
<item>射手</item>
<item>辅助</item>
<item>战士</item>
<item>坦克</item>
</string-array>
</resources>
获取选择的值也很简单
final Spinner hero_category = findViewById(R.id.hero_add_category);
//获取值
hero_category.getSelectedItem().toString();
选择生存能力等值
这里使用了一个GitHub的第三方控件org.adw.library.widgets.discreteseekbar.DiscreteSeekBar
<org.adw.library.widgets.discreteseekbar.DiscreteSeekBar
android:id="@+id/hero_add_viability"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:dsb_min="1"
app:dsb_max="9"
app:dsb_progressColor="@color/lightblue"
android:layout_marginEnd="10dp"
/>
HeroAdd.java
中获取选中的值
final DiscreteSeekBar hero_viability = findViewById(R.id.hero_add_viability);
hero_viability.getProgress()
保存结果
点击菜单中的保存按钮的事件
这里有三种情况
- 英雄名为空
- 英雄名重复(跟传进来的已有英雄数组比较)
- 添加成功
三种情况都会弹出Toast
提示
//设置菜单点击事件
toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem menuItem) {
switch (menuItem.getItemId()) {
//保存
case R.id.action_hero_add_save:
if(hero_name.getText().toString().isEmpty()){
Toast.makeText(HeroAdd.this, "英雄名不能为空!", Toast.LENGTH_SHORT).show();
return false;
}
for(String i : hero_names){
if(i.equals(hero_name.getText().toString())){
Toast.makeText(HeroAdd.this, "英雄名重复!", Toast.LENGTH_SHORT).show();
return false;
}
}
add_hero = new Hero(hero_name.getText().toString(), image_uri, hero_alias.getText().toString(),hero_category.getSelectedItem().toString(),
hero_viability.getProgress(),hero_attack_damage.getProgress(),hero_skill_damage.getProgress(), hero_difficulty.getProgress(),voice_uri,icon_uri,false);
add_hero.setAdded(true);
EventBus.getDefault().post(add_hero);
finish();
Toast.makeText(HeroAdd.this, "添加成功", Toast.LENGTH_SHORT).show();
break;
......
但是主要问题是如何把添加的英雄传给主界面呢(因为这里不做英雄数据库的操作),
或者说通知主界面新添加了一个英雄呢(点击ToolBar
的返回按钮或者虚拟按键返回会放弃添加英雄)
最简单的方法当然是EventBus
而且还有用到了Hero
类的一个属性
private Boolean added = false;//true表示是新加的
向下面这样子把新加的Hero
传递回去,并且调用finish
方法表示结束该Acitivity
这样子只要在Fragment1.java
中的EventBus
接收方法中判断英雄的added
属性是否为true
就可以判断是否要把这个英雄加到数据库里了
之所以要设置added这个变量是为了跟后面的deleted,modified分别表示的已删除,已修改作区分
根本宗旨就是数据库的更新全部放在英雄主界面(Fragment1)其它页面只能传递参数告诉它要如何更改数据库
add_hero.setAdded(true);
EventBus.getDefault().post(add_hero);
finish();
更新数据库
如上面讨论的这个得在Fragment1.java
中实现
添加EventBus
的处理方法
判断传递过来的Hero
的added
属性是否为true
,是的话就添加到Adapter
中
还要添加到SQLiteHelper
中持久存储
//EventBus
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(Hero h) {
if(h.getAdded()){
h.setAdded(false);
searchAdapter.add(h.getName());
//searchAdapter.notifyDataSetChanged();
mAdapter.addNewItem(h);
mySQLiteHelper.addHero(h);
//更新当前选中的英雄类型的列表
mAdapter.updateWithCategory(mySQLiteHelper.getAllHeroes(), ((RadioButton)view.findViewById(hero_category_select.getCheckedRadioButtonId())).getText().toString());
Log.d("添加英雄", "onMessageEvent: add" + h.getName());
} else if
.......
};
//记得注册
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
EventBus.getDefault().register(this);
......