@ -0,0 +1,127 @@ |
||||
package xyz.fycz.myreader.base; |
||||
|
||||
import android.content.Intent; |
||||
import android.os.Bundle; |
||||
|
||||
import androidx.annotation.LayoutRes; |
||||
import androidx.annotation.Nullable; |
||||
import androidx.appcompat.app.ActionBar; |
||||
import androidx.appcompat.app.AppCompatActivity; |
||||
import androidx.appcompat.widget.Toolbar; |
||||
import androidx.core.content.ContextCompat; |
||||
import butterknife.ButterKnife; |
||||
import butterknife.Unbinder; |
||||
|
||||
import io.reactivex.disposables.CompositeDisposable; |
||||
import io.reactivex.disposables.Disposable; |
||||
import xyz.fycz.myreader.R; |
||||
import xyz.fycz.myreader.util.StatusBarCompat; |
||||
|
||||
/** |
||||
* @author fengyue |
||||
* @date 2020/8/12 20:02 |
||||
*/ |
||||
public abstract class BaseActivity2 extends AppCompatActivity { |
||||
private static final int INVALID_VAL = -1; |
||||
|
||||
protected CompositeDisposable mDisposable; |
||||
//ButterKnife
|
||||
private Toolbar mToolbar; |
||||
|
||||
private Unbinder unbinder; |
||||
/****************************abstract area*************************************/ |
||||
|
||||
@LayoutRes |
||||
protected abstract int getContentId(); |
||||
|
||||
/************************init area************************************/ |
||||
protected void addDisposable(Disposable d){ |
||||
if (mDisposable == null){ |
||||
mDisposable = new CompositeDisposable(); |
||||
} |
||||
mDisposable.add(d); |
||||
} |
||||
|
||||
/** |
||||
* 配置Toolbar |
||||
* @param toolbar |
||||
*/ |
||||
protected void setUpToolbar(Toolbar toolbar){ |
||||
} |
||||
|
||||
protected void initData(Bundle savedInstanceState){ |
||||
} |
||||
/** |
||||
* 初始化零件 |
||||
*/ |
||||
protected void initWidget() { |
||||
|
||||
} |
||||
/** |
||||
* 初始化点击事件 |
||||
*/ |
||||
protected void initClick(){ |
||||
} |
||||
/** |
||||
* 逻辑使用区 |
||||
*/ |
||||
protected void processLogic(){ |
||||
} |
||||
|
||||
/*************************lifecycle area*****************************************************/ |
||||
|
||||
@Override |
||||
protected void onCreate(@Nullable Bundle savedInstanceState) { |
||||
super.onCreate(savedInstanceState); |
||||
setContentView(getContentId()); |
||||
initData(savedInstanceState); |
||||
unbinder = ButterKnife.bind(this); |
||||
initToolbar(); |
||||
initWidget(); |
||||
initClick(); |
||||
processLogic(); |
||||
} |
||||
|
||||
private void initToolbar(){ |
||||
//更严谨是通过反射判断是否存在Toolbar
|
||||
mToolbar = findViewById(R.id.toolbar); |
||||
if (mToolbar != null){ |
||||
supportActionBar(mToolbar); |
||||
setUpToolbar(mToolbar); |
||||
} |
||||
} |
||||
|
||||
|
||||
@Override |
||||
protected void onDestroy() { |
||||
super.onDestroy(); |
||||
unbinder.unbind(); |
||||
if (mDisposable != null){ |
||||
mDisposable.dispose(); |
||||
} |
||||
} |
||||
|
||||
/**************************used method area*******************************************/ |
||||
|
||||
protected void startActivity(Class<? extends AppCompatActivity> activity){ |
||||
Intent intent = new Intent(this,activity); |
||||
startActivity(intent); |
||||
} |
||||
|
||||
protected ActionBar supportActionBar(Toolbar toolbar){ |
||||
setSupportActionBar(toolbar); |
||||
ActionBar actionBar = getSupportActionBar(); |
||||
if (actionBar != null){ |
||||
actionBar.setDisplayHomeAsUpEnabled(true); |
||||
actionBar.setDisplayShowHomeEnabled(true); |
||||
} |
||||
mToolbar.setNavigationOnClickListener( |
||||
(v) -> finish() |
||||
); |
||||
return actionBar; |
||||
} |
||||
|
||||
protected void setStatusBarColor(int statusColor){ |
||||
StatusBarCompat.compat(this, ContextCompat.getColor(this, statusColor)); |
||||
} |
||||
} |
@ -0,0 +1,104 @@ |
||||
package xyz.fycz.myreader.base; |
||||
|
||||
import android.os.Bundle; |
||||
|
||||
import android.view.LayoutInflater; |
||||
import android.view.View; |
||||
import android.view.ViewGroup; |
||||
import androidx.annotation.LayoutRes; |
||||
import androidx.annotation.Nullable; |
||||
import androidx.fragment.app.Fragment; |
||||
import butterknife.ButterKnife; |
||||
import butterknife.Unbinder; |
||||
import io.reactivex.disposables.CompositeDisposable; |
||||
import io.reactivex.disposables.Disposable; |
||||
|
||||
/** |
||||
* @author fengyue |
||||
* @date 2020/8/12 20:02 |
||||
*/ |
||||
|
||||
public abstract class BaseFragment extends Fragment { |
||||
|
||||
protected CompositeDisposable mDisposable; |
||||
|
||||
private View root = null; |
||||
private Unbinder unbinder; |
||||
|
||||
@LayoutRes |
||||
protected abstract int getContentId(); |
||||
|
||||
/*******************************init area*********************************/ |
||||
protected void addDisposable(Disposable d){ |
||||
if (mDisposable == null){ |
||||
mDisposable = new CompositeDisposable(); |
||||
} |
||||
mDisposable.add(d); |
||||
} |
||||
|
||||
|
||||
protected void initData(Bundle savedInstanceState){ |
||||
} |
||||
|
||||
/** |
||||
* 初始化点击事件 |
||||
*/ |
||||
protected void initClick(){ |
||||
} |
||||
|
||||
/** |
||||
* 逻辑使用区 |
||||
*/ |
||||
protected void processLogic(){ |
||||
} |
||||
|
||||
/** |
||||
* 初始化零件 |
||||
*/ |
||||
protected void initWidget(Bundle savedInstanceState){ |
||||
} |
||||
|
||||
/******************************lifecycle area*****************************************/ |
||||
@Nullable |
||||
@Override |
||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { |
||||
int resId = getContentId(); |
||||
root = inflater.inflate(resId,container,false); |
||||
return root; |
||||
} |
||||
|
||||
@Override |
||||
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { |
||||
super.onViewCreated(view, savedInstanceState); |
||||
initData(savedInstanceState); |
||||
unbinder = ButterKnife.bind(this,root); |
||||
initWidget(savedInstanceState); |
||||
initClick(); |
||||
processLogic(); |
||||
} |
||||
|
||||
@Override |
||||
public void onDetach() { |
||||
super.onDetach(); |
||||
|
||||
unbinder.unbind(); |
||||
|
||||
if (mDisposable != null){ |
||||
mDisposable.clear(); |
||||
} |
||||
} |
||||
|
||||
/**************************公共类*******************************************/ |
||||
public String getName(){ |
||||
return getClass().getName(); |
||||
} |
||||
|
||||
protected <VT> VT getViewById(int id){ |
||||
if (root == null){ |
||||
return null; |
||||
} |
||||
return (VT) root.findViewById(id); |
||||
} |
||||
} |
||||
|
||||
|
@ -0,0 +1,154 @@ |
||||
package xyz.fycz.myreader.base; |
||||
|
||||
import android.os.Handler; |
||||
import android.view.View; |
||||
import android.view.ViewGroup; |
||||
import androidx.recyclerview.widget.RecyclerView; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.Collections; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* @author fengyue |
||||
* @date 2020/8/12 20:02 |
||||
*/ |
||||
|
||||
public abstract class BaseListAdapter<T> extends RecyclerView.Adapter<RecyclerView.ViewHolder>{ |
||||
|
||||
private static final String TAG = "BaseListAdapter"; |
||||
/*common statement*/ |
||||
protected final List<T> mList = new ArrayList<>(); |
||||
protected OnItemClickListener mClickListener; |
||||
protected OnItemLongClickListener mLongClickListener; |
||||
|
||||
/************************abstract area************************/ |
||||
protected abstract IViewHolder<T> createViewHolder(int viewType); |
||||
|
||||
/*************************rewrite logic area***************************************/ |
||||
@Override |
||||
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { |
||||
IViewHolder<T> viewHolder = createViewHolder(viewType); |
||||
|
||||
View view = viewHolder.createItemView(parent); |
||||
//初始化
|
||||
RecyclerView.ViewHolder holder = new BaseViewHolder(view, viewHolder); |
||||
return holder; |
||||
} |
||||
|
||||
@Override |
||||
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { |
||||
//防止别人直接使用RecyclerView.ViewHolder调用该方法
|
||||
if (!(holder instanceof BaseViewHolder)) |
||||
throw new IllegalArgumentException("The ViewHolder item must extend BaseViewHolder"); |
||||
|
||||
IViewHolder<T> iHolder = ((BaseViewHolder) holder).holder; |
||||
iHolder.onBind(getItem(position),position); |
||||
|
||||
//设置点击事件
|
||||
holder.itemView.setOnClickListener((v)->{ |
||||
if (mClickListener != null){ |
||||
mClickListener.onItemClick(v,position); |
||||
} |
||||
//adapter监听点击事件
|
||||
iHolder.onClick(); |
||||
onItemClick(v,position); |
||||
}); |
||||
//设置长点击事件
|
||||
holder.itemView.setOnLongClickListener( |
||||
(v) -> { |
||||
boolean isClicked = false; |
||||
if (mLongClickListener != null){ |
||||
isClicked = mLongClickListener.onItemLongClick(v,position); |
||||
} |
||||
//Adapter监听长点击事件
|
||||
onItemLongClick(v,position); |
||||
return isClicked; |
||||
} |
||||
); |
||||
} |
||||
|
||||
@Override |
||||
public int getItemCount() { |
||||
return mList.size(); |
||||
} |
||||
|
||||
protected void onItemClick(View v, int pos){ |
||||
} |
||||
|
||||
protected void onItemLongClick(View v, int pos){ |
||||
} |
||||
|
||||
/******************************public area***********************************/ |
||||
|
||||
public void setOnItemClickListener(OnItemClickListener mListener) { |
||||
this.mClickListener = mListener; |
||||
} |
||||
|
||||
public void setOnItemLongClickListener(OnItemLongClickListener mListener){ |
||||
this.mLongClickListener = mListener; |
||||
} |
||||
|
||||
public void addItem(T value){ |
||||
mList.add(value); |
||||
notifyDataSetChanged(); |
||||
} |
||||
|
||||
public void addItem(int index,T value){ |
||||
mList.add(index, value); |
||||
notifyDataSetChanged(); |
||||
} |
||||
|
||||
public void addItems(List<T> values){ |
||||
mList.addAll(values); |
||||
|
||||
Handler handler = new Handler(); |
||||
handler.post(new Runnable() { |
||||
@Override |
||||
public void run() { |
||||
notifyDataSetChanged(); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
public void removeItem(T value){ |
||||
mList.remove(value); |
||||
notifyDataSetChanged(); |
||||
} |
||||
|
||||
public void removeItems(List<T> value){ |
||||
mList.removeAll(value); |
||||
notifyDataSetChanged(); |
||||
} |
||||
|
||||
public T getItem(int position){ |
||||
return mList.get(position); |
||||
} |
||||
|
||||
public List<T> getItems(){ |
||||
return Collections.unmodifiableList(mList); |
||||
} |
||||
|
||||
public int getItemSize(){ |
||||
return mList.size(); |
||||
} |
||||
|
||||
public void refreshItems(List<T> list){ |
||||
mList.clear(); |
||||
mList.addAll(list); |
||||
notifyDataSetChanged(); |
||||
} |
||||
|
||||
public void clear(){ |
||||
mList.clear(); |
||||
} |
||||
|
||||
/***************************inner class area***********************************/ |
||||
public interface OnItemClickListener{ |
||||
void onItemClick(View view, int pos); |
||||
} |
||||
|
||||
public interface OnItemLongClickListener{ |
||||
boolean onItemLongClick(View view, int pos); |
||||
} |
||||
} |
@ -0,0 +1,86 @@ |
||||
package xyz.fycz.myreader.base; |
||||
|
||||
|
||||
import androidx.fragment.app.Fragment; |
||||
import androidx.fragment.app.FragmentManager; |
||||
import androidx.fragment.app.FragmentPagerAdapter; |
||||
import androidx.viewpager.widget.ViewPager; |
||||
import butterknife.BindView; |
||||
import com.google.android.material.tabs.TabLayout; |
||||
import xyz.fycz.myreader.R; |
||||
|
||||
import java.util.List; |
||||
|
||||
/** |
||||
* @author fengyue |
||||
* @date 2020/8/12 20:02 |
||||
*/ |
||||
|
||||
public abstract class BaseTabActivity extends BaseActivity2 { |
||||
/**************View***************/ |
||||
@BindView(R.id.tab_tl_indicator) |
||||
protected TabLayout mTlIndicator; |
||||
@BindView(R.id.tab_vp) |
||||
protected ViewPager mVp; |
||||
/************Params*******************/ |
||||
private List<Fragment> mFragmentList; |
||||
private List<String> mTitleList; |
||||
|
||||
/**************abstract***********/ |
||||
protected abstract List<Fragment> createTabFragments(); |
||||
protected abstract List<String> createTabTitles(); |
||||
|
||||
/*****************rewrite method***************************/ |
||||
@Override |
||||
protected void initWidget() { |
||||
super.initWidget(); |
||||
setUpTabLayout(); |
||||
} |
||||
|
||||
private void setUpTabLayout(){ |
||||
mFragmentList = createTabFragments(); |
||||
mTitleList = createTabTitles(); |
||||
|
||||
checkParamsIsRight(); |
||||
|
||||
TabFragmentPageAdapter adapter = new TabFragmentPageAdapter(getSupportFragmentManager()); |
||||
mVp.setAdapter(adapter); |
||||
mVp.setOffscreenPageLimit(3); |
||||
mTlIndicator.setupWithViewPager(mVp); |
||||
} |
||||
|
||||
/** |
||||
* 检查输入的参数是否正确。即Fragment和title是成对的。 |
||||
*/ |
||||
private void checkParamsIsRight(){ |
||||
if (mFragmentList == null || mTitleList == null){ |
||||
throw new IllegalArgumentException("fragmentList or titleList doesn't have null"); |
||||
} |
||||
|
||||
if (mFragmentList.size() != mTitleList.size()) |
||||
throw new IllegalArgumentException("fragment and title size must equal"); |
||||
} |
||||
|
||||
/******************inner class*****************/ |
||||
class TabFragmentPageAdapter extends FragmentPagerAdapter { |
||||
|
||||
public TabFragmentPageAdapter(FragmentManager fm) { |
||||
super(fm); |
||||
} |
||||
|
||||
@Override |
||||
public Fragment getItem(int position) { |
||||
return mFragmentList.get(position); |
||||
} |
||||
|
||||
@Override |
||||
public int getCount() { |
||||
return mFragmentList.size(); |
||||
} |
||||
|
||||
@Override |
||||
public CharSequence getPageTitle(int position) { |
||||
return mTitleList.get(position); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,19 @@ |
||||
package xyz.fycz.myreader.base; |
||||
|
||||
import android.view.View; |
||||
import androidx.recyclerview.widget.RecyclerView; |
||||
|
||||
/** |
||||
* @author fengyue |
||||
* @date 2020/8/12 20:02 |
||||
*/ |
||||
|
||||
public class BaseViewHolder<T> extends RecyclerView.ViewHolder{ |
||||
public IViewHolder<T> holder; |
||||
|
||||
public BaseViewHolder(View itemView, IViewHolder<T> holder) { |
||||
super(itemView); |
||||
this.holder = holder; |
||||
holder.initView(); |
||||
} |
||||
} |
@ -0,0 +1,16 @@ |
||||
package xyz.fycz.myreader.base; |
||||
|
||||
import android.view.View; |
||||
import android.view.ViewGroup; |
||||
|
||||
/** |
||||
* @author fengyue |
||||
* @date 2020/8/12 20:02 |
||||
*/ |
||||
|
||||
public interface IViewHolder<T> { |
||||
View createItemView(ViewGroup parent); |
||||
void initView(); |
||||
void onBind(T data,int pos); |
||||
void onClick(); |
||||
} |
@ -0,0 +1,43 @@ |
||||
package xyz.fycz.myreader.base; |
||||
|
||||
import android.content.Context; |
||||
import android.view.LayoutInflater; |
||||
import android.view.View; |
||||
import android.view.ViewGroup; |
||||
|
||||
/** |
||||
* @author fengyue |
||||
* @date 2020/8/12 20:02 |
||||
*/ |
||||
|
||||
public abstract class ViewHolderImpl<T> implements IViewHolder<T> { |
||||
private View view; |
||||
private Context context; |
||||
/****************************************************/ |
||||
protected abstract int getItemLayoutId(); |
||||
|
||||
|
||||
@Override |
||||
public View createItemView(ViewGroup parent) { |
||||
view = LayoutInflater.from(parent.getContext()) |
||||
.inflate(getItemLayoutId(), parent, false); |
||||
context = parent.getContext(); |
||||
return view; |
||||
} |
||||
|
||||
protected <V extends View> V findById(int id){ |
||||
return (V) view.findViewById(id); |
||||
} |
||||
|
||||
protected Context getContext(){ |
||||
return context; |
||||
} |
||||
|
||||
protected View getItemView(){ |
||||
return view; |
||||
} |
||||
|
||||
@Override |
||||
public void onClick() { |
||||
} |
||||
} |
@ -0,0 +1,87 @@ |
||||
package xyz.fycz.myreader.ui.filesys; |
||||
|
||||
|
||||
|
||||
import xyz.fycz.myreader.base.BaseFragment; |
||||
|
||||
import java.io.File; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* @author fengyue |
||||
* @date 2020/8/12 20:02 |
||||
* FileSystemActivity的基础Fragment类 |
||||
*/ |
||||
|
||||
public abstract class BaseFileFragment extends BaseFragment { |
||||
|
||||
protected FileSystemAdapter mAdapter; |
||||
protected OnFileCheckedListener mListener; |
||||
protected boolean isCheckedAll; |
||||
|
||||
//设置当前列表为全选
|
||||
public void setCheckedAll(boolean checkedAll){ |
||||
if (mAdapter == null) return; |
||||
|
||||
isCheckedAll = checkedAll; |
||||
mAdapter.setCheckedAll(checkedAll); |
||||
} |
||||
|
||||
public void setChecked(boolean checked){ |
||||
isCheckedAll = checked; |
||||
} |
||||
|
||||
//当前fragment是否全选
|
||||
public boolean isCheckedAll() { |
||||
return isCheckedAll; |
||||
} |
||||
|
||||
//获取被选中的数量
|
||||
public int getCheckedCount(){ |
||||
if (mAdapter == null) return 0; |
||||
return mAdapter.getCheckedCount(); |
||||
} |
||||
|
||||
//获取被选中的文件列表
|
||||
public List<File> getCheckedFiles(){ |
||||
return mAdapter != null ? mAdapter.getCheckedFiles() : null; |
||||
} |
||||
|
||||
//获取文件的总数
|
||||
public int getFileCount(){ |
||||
return mAdapter != null ? mAdapter.getItemCount() : null; |
||||
} |
||||
|
||||
//获取可点击的文件的数量
|
||||
public int getCheckableCount(){ |
||||
if (mAdapter == null) return 0; |
||||
return mAdapter.getCheckableCount(); |
||||
} |
||||
|
||||
/** |
||||
* 删除选中的文件 |
||||
*/ |
||||
public void deleteCheckedFiles(){ |
||||
//删除选中的文件
|
||||
List<File> files = getCheckedFiles(); |
||||
//删除显示的文件列表
|
||||
mAdapter.removeItems(files); |
||||
//删除选中的文件
|
||||
for (File file : files){ |
||||
if (file.exists()){ |
||||
file.delete(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
//设置文件点击监听事件
|
||||
public void setOnFileCheckedListener(OnFileCheckedListener listener){ |
||||
mListener = listener; |
||||
} |
||||
|
||||
//文件点击监听
|
||||
public interface OnFileCheckedListener { |
||||
void onItemCheckedChange(boolean isChecked); |
||||
void onCategoryChanged(); |
||||
} |
||||
} |
@ -0,0 +1,176 @@ |
||||
package xyz.fycz.myreader.ui.filesys; |
||||
|
||||
import android.os.Bundle; |
||||
import android.os.Environment; |
||||
|
||||
import android.widget.TextView; |
||||
import androidx.recyclerview.widget.LinearLayoutManager; |
||||
import androidx.recyclerview.widget.RecyclerView; |
||||
import butterknife.BindView; |
||||
import xyz.fycz.myreader.R; |
||||
import xyz.fycz.myreader.greendao.service.BookService; |
||||
import xyz.fycz.myreader.util.FileStack; |
||||
import xyz.fycz.myreader.util.utils.FileUtils; |
||||
import xyz.fycz.myreader.widget.DividerItemDecoration; |
||||
|
||||
|
||||
import java.io.File; |
||||
import java.io.FileFilter; |
||||
import java.util.*; |
||||
|
||||
/** |
||||
* @author fengyue |
||||
* @date 2020/8/12 20:02 |
||||
*/ |
||||
|
||||
public class FileCategoryFragment extends BaseFileFragment { |
||||
private static final String TAG = "FileCategoryFragment"; |
||||
@BindView(R.id.file_category_tv_path) |
||||
TextView mTvPath; |
||||
@BindView(R.id.file_category_tv_back_last) |
||||
TextView mTvBackLast; |
||||
@BindView(R.id.file_category_rv_content) |
||||
RecyclerView mRvContent; |
||||
|
||||
private FileStack mFileStack; |
||||
@Override |
||||
protected int getContentId() { |
||||
return R.layout.fragment_file_category; |
||||
} |
||||
|
||||
@Override |
||||
protected void initWidget(Bundle savedInstanceState) { |
||||
super.initWidget(savedInstanceState); |
||||
mFileStack = new FileStack(); |
||||
setUpAdapter(); |
||||
} |
||||
|
||||
private void setUpAdapter(){ |
||||
mAdapter = new FileSystemAdapter(); |
||||
mRvContent.setLayoutManager(new LinearLayoutManager(getContext())); |
||||
mRvContent.addItemDecoration(new DividerItemDecoration(getContext())); |
||||
mRvContent.setAdapter(mAdapter); |
||||
} |
||||
|
||||
@Override |
||||
protected void initClick() { |
||||
super.initClick(); |
||||
mAdapter.setOnItemClickListener( |
||||
(view, pos) -> { |
||||
File file = mAdapter.getItem(pos); |
||||
if (file.isDirectory()){ |
||||
//保存当前信息。
|
||||
FileStack.FileSnapshot snapshot = new FileStack.FileSnapshot(); |
||||
snapshot.filePath = mTvPath.getText().toString(); |
||||
snapshot.files = new ArrayList<>(mAdapter.getItems()); |
||||
snapshot.scrollOffset = mRvContent.computeVerticalScrollOffset(); |
||||
mFileStack.push(snapshot); |
||||
//切换下一个文件
|
||||
toggleFileTree(file); |
||||
} |
||||
else { |
||||
|
||||
//如果是已加载的文件,则点击事件无效。
|
||||
String path = mAdapter.getItem(pos).getAbsolutePath(); |
||||
if (BookService.getInstance().findBookByPath(path) != null){ |
||||
return; |
||||
} |
||||
//点击选中
|
||||
mAdapter.setCheckedItem(pos); |
||||
//反馈
|
||||
if (mListener != null){ |
||||
mListener.onItemCheckedChange(mAdapter.getItemIsChecked(pos)); |
||||
} |
||||
} |
||||
} |
||||
); |
||||
|
||||
mTvBackLast.setOnClickListener( |
||||
(v) -> { |
||||
FileStack.FileSnapshot snapshot = mFileStack.pop(); |
||||
int oldScrollOffset = mRvContent.computeHorizontalScrollOffset(); |
||||
if (snapshot == null) return; |
||||
mTvPath.setText(snapshot.filePath); |
||||
mAdapter.refreshItems(snapshot.files); |
||||
mRvContent.scrollBy(0,snapshot.scrollOffset - oldScrollOffset); |
||||
//反馈
|
||||
if (mListener != null){ |
||||
mListener.onCategoryChanged(); |
||||
} |
||||
} |
||||
); |
||||
|
||||
} |
||||
|
||||
@Override |
||||
protected void processLogic() { |
||||
super.processLogic(); |
||||
File root = Environment.getExternalStorageDirectory(); |
||||
toggleFileTree(root); |
||||
} |
||||
|
||||
private void toggleFileTree(File file){ |
||||
//路径名
|
||||
mTvPath.setText(getString(R.string.file_path,file.getPath())); |
||||
//获取数据
|
||||
File[] files = file.listFiles(new SimpleFileFilter()); |
||||
//转换成List
|
||||
List<File> rootFiles = Arrays.asList(files); |
||||
//排序
|
||||
Collections.sort(rootFiles, new FileComparator()); |
||||
//加入
|
||||
mAdapter.refreshItems(rootFiles); |
||||
//反馈
|
||||
if (mListener != null){ |
||||
mListener.onCategoryChanged(); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public int getFileCount(){ |
||||
int count = 0; |
||||
Set<Map.Entry<File, Boolean>> entrys = mAdapter.getCheckMap().entrySet(); |
||||
for (Map.Entry<File, Boolean> entry:entrys){ |
||||
if (!entry.getKey().isDirectory()){ |
||||
++count; |
||||
} |
||||
} |
||||
return count; |
||||
} |
||||
|
||||
public static class FileComparator implements Comparator<File> { |
||||
@Override |
||||
public int compare(File o1, File o2){ |
||||
if (o1.isDirectory() && o2.isFile()) { |
||||
return -1; |
||||
} |
||||
if (o2.isDirectory() && o1.isFile()) { |
||||
return 1; |
||||
} |
||||
return o1.getName().compareToIgnoreCase(o2.getName()); |
||||
} |
||||
} |
||||
|
||||
public static class SimpleFileFilter implements FileFilter { |
||||
@Override |
||||
public boolean accept(File pathname) { |
||||
if (pathname.getName().startsWith(".")){ |
||||
return false; |
||||
} |
||||
//文件夹内部数量为0
|
||||
if (pathname.isDirectory() && (pathname.list() == null || pathname.list().length == 0)) { |
||||
return false; |
||||
} |
||||
|
||||
/** |
||||
* 现在只支持TXT文件的显示 |
||||
*/ |
||||
//文件内容为空,或者不以txt为开头
|
||||
if (!pathname.isDirectory() && |
||||
(pathname.length() == 0 || !pathname.getName().endsWith(FileUtils.SUFFIX_TXT))){ |
||||
return false; |
||||
} |
||||
return true; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,103 @@ |
||||
package xyz.fycz.myreader.ui.filesys; |
||||
|
||||
import android.view.View; |
||||
import android.widget.CheckBox; |
||||
import android.widget.ImageView; |
||||
import android.widget.LinearLayout; |
||||
import android.widget.TextView; |
||||
import xyz.fycz.myreader.R; |
||||
import xyz.fycz.myreader.base.ViewHolderImpl; |
||||
import xyz.fycz.myreader.common.APPCONST; |
||||
import xyz.fycz.myreader.greendao.service.BookService; |
||||
import xyz.fycz.myreader.util.utils.FileUtils; |
||||
import xyz.fycz.myreader.util.utils.StringUtils; |
||||
|
||||
|
||||
import java.io.File; |
||||
import java.util.HashMap; |
||||
|
||||
/** |
||||
* @author fengyue |
||||
* @date 2020/8/12 20:02 |
||||
*/ |
||||
|
||||
public class FileHolder extends ViewHolderImpl<File> { |
||||
private ImageView mIvIcon; |
||||
private CheckBox mCbSelect; |
||||
private TextView mTvName; |
||||
private LinearLayout mLlBrief; |
||||
private TextView mTvTag; |
||||
private TextView mTvSize; |
||||
private TextView mTvDate; |
||||
private TextView mTvSubCount; |
||||
|
||||
private HashMap<File, Boolean> mSelectedMap; |
||||
public FileHolder(HashMap<File, Boolean> selectedMap){ |
||||
mSelectedMap = selectedMap; |
||||
} |
||||
|
||||
@Override |
||||
public void initView() { |
||||
mIvIcon = findById(R.id.file_iv_icon); |
||||
mCbSelect = findById(R.id.file_cb_select); |
||||
mTvName = findById(R.id.file_tv_name); |
||||
mLlBrief = findById(R.id.file_ll_brief); |
||||
mTvTag = findById(R.id.file_tv_tag); |
||||
mTvSize = findById(R.id.file_tv_size); |
||||
mTvDate = findById(R.id.file_tv_date); |
||||
mTvSubCount = findById(R.id.file_tv_sub_count); |
||||
} |
||||
|
||||
@Override |
||||
public void onBind(File data, int pos) { |
||||
//判断是文件还是文件夹
|
||||
if (data.isDirectory()){ |
||||
setFolder(data); |
||||
} |
||||
else { |
||||
setFile(data); |
||||
} |
||||
} |
||||
|
||||
private void setFile(File file){ |
||||
//选择
|
||||
|
||||
if (BookService.getInstance().findBookByPath(file.getAbsolutePath()) != null){ |
||||
mIvIcon.setImageResource(R.drawable.ic_file_loaded); |
||||
mIvIcon.setVisibility(View.VISIBLE); |
||||
mCbSelect.setVisibility(View.GONE); |
||||
} |
||||
else { |
||||
boolean isSelected = mSelectedMap.get(file); |
||||
mCbSelect.setChecked(isSelected); |
||||
mIvIcon.setVisibility(View.GONE); |
||||
mCbSelect.setVisibility(View.VISIBLE); |
||||
} |
||||
|
||||
mLlBrief.setVisibility(View.VISIBLE); |
||||
mTvSubCount.setVisibility(View.GONE); |
||||
|
||||
mTvName.setText(file.getName()); |
||||
mTvSize.setText(FileUtils.getFileSize(file.length())); |
||||
mTvDate.setText(StringUtils.dateConvert(file.lastModified(), APPCONST.FORMAT_FILE_DATE)); |
||||
} |
||||
|
||||
public void setFolder(File folder){ |
||||
//图片
|
||||
mIvIcon.setVisibility(View.VISIBLE); |
||||
mCbSelect.setVisibility(View.GONE); |
||||
mIvIcon.setImageResource(R.drawable.ic_folder); |
||||
//名字
|
||||
mTvName.setText(folder.getName()); |
||||
//介绍
|
||||
mLlBrief.setVisibility(View.GONE); |
||||
mTvSubCount.setVisibility(View.VISIBLE); |
||||
|
||||
mTvSubCount.setText(getContext().getString(R.string.file_sub_count, folder.list().length)); |
||||
} |
||||
|
||||
@Override |
||||
protected int getItemLayoutId() { |
||||
return R.layout.item_file; |
||||
} |
||||
} |
@ -0,0 +1,266 @@ |
||||
package xyz.fycz.myreader.ui.filesys; |
||||
|
||||
|
||||
import android.widget.Button; |
||||
import android.widget.CheckBox; |
||||
import androidx.appcompat.widget.Toolbar; |
||||
import androidx.fragment.app.Fragment; |
||||
import androidx.viewpager.widget.ViewPager; |
||||
import butterknife.BindView; |
||||
import xyz.fycz.myreader.R; |
||||
import xyz.fycz.myreader.base.BaseTabActivity; |
||||
import xyz.fycz.myreader.creator.DialogCreator; |
||||
import xyz.fycz.myreader.enums.BookSource; |
||||
import xyz.fycz.myreader.greendao.entity.Book; |
||||
import xyz.fycz.myreader.greendao.service.BookService; |
||||
import xyz.fycz.myreader.util.TextHelper; |
||||
|
||||
|
||||
import java.io.File; |
||||
import java.util.ArrayList; |
||||
import java.util.Arrays; |
||||
import java.util.List; |
||||
|
||||
|
||||
/** |
||||
* @author fengyue |
||||
* @date 2020/8/12 20:02 |
||||
*/ |
||||
|
||||
public class FileSystemActivity extends BaseTabActivity { |
||||
private static final String TAG = "FileSystemActivity"; |
||||
|
||||
@BindView(R.id.file_system_cb_selected_all) |
||||
CheckBox mCbSelectAll; |
||||
@BindView(R.id.file_system_btn_delete) |
||||
Button mBtnDelete; |
||||
@BindView(R.id.file_system_btn_add_book) |
||||
Button mBtnAddBook; |
||||
|
||||
private LocalBookFragment mLocalFragment; |
||||
private FileCategoryFragment mCategoryFragment; |
||||
private BaseFileFragment mCurFragment; |
||||
|
||||
private BaseFileFragment.OnFileCheckedListener mListener = new BaseFileFragment.OnFileCheckedListener() { |
||||
@Override |
||||
public void onItemCheckedChange(boolean isChecked) { |
||||
changeMenuStatus(); |
||||
} |
||||
|
||||
@Override |
||||
public void onCategoryChanged() { |
||||
//状态归零
|
||||
mCurFragment.setCheckedAll(false); |
||||
//改变菜单
|
||||
changeMenuStatus(); |
||||
//改变是否能够全选
|
||||
changeCheckedAllStatus(); |
||||
} |
||||
}; |
||||
|
||||
@Override |
||||
protected List<Fragment> createTabFragments() { |
||||
mLocalFragment = new LocalBookFragment(); |
||||
mCategoryFragment = new FileCategoryFragment(); |
||||
return Arrays.asList(mLocalFragment,mCategoryFragment); |
||||
} |
||||
|
||||
@Override |
||||
protected List<String> createTabTitles() { |
||||
return Arrays.asList("智能导入","手机目录"); |
||||
} |
||||
|
||||
@Override |
||||
protected int getContentId() { |
||||
return R.layout.activity_file_system; |
||||
} |
||||
|
||||
@Override |
||||
protected void setUpToolbar(Toolbar toolbar) { |
||||
super.setUpToolbar(toolbar); |
||||
setStatusBarColor(R.color.sys_line); |
||||
getSupportActionBar().setTitle("添加本地"); |
||||
} |
||||
|
||||
@Override |
||||
protected void initClick() { |
||||
super.initClick(); |
||||
mCbSelectAll.setOnClickListener( |
||||
(view) -> { |
||||
//设置全选状态
|
||||
boolean isChecked = mCbSelectAll.isChecked(); |
||||
mCurFragment.setCheckedAll(isChecked); |
||||
//改变菜单状态
|
||||
changeMenuStatus(); |
||||
} |
||||
); |
||||
|
||||
mVp.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { |
||||
@Override |
||||
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { |
||||
|
||||
} |
||||
|
||||
@Override |
||||
public void onPageSelected(int position) { |
||||
if (position == 0){ |
||||
mCurFragment = mLocalFragment; |
||||
} |
||||
else { |
||||
mCurFragment = mCategoryFragment; |
||||
} |
||||
//改变菜单状态
|
||||
changeMenuStatus(); |
||||
//改变是否能够全选
|
||||
changeCheckedAllStatus(); |
||||
} |
||||
|
||||
@Override |
||||
public void onPageScrollStateChanged(int state) { |
||||
|
||||
} |
||||
}); |
||||
|
||||
mBtnAddBook.setOnClickListener( |
||||
(v) -> { |
||||
//获取选中的文件
|
||||
List<File> files = mCurFragment.getCheckedFiles(); |
||||
//转换成Book,并存储
|
||||
List<Book> books = convertBook(files); |
||||
BookService.getInstance() |
||||
.addBooks(books); |
||||
//设置HashMap为false
|
||||
mCurFragment.setCheckedAll(false); |
||||
//改变菜单状态
|
||||
changeMenuStatus(); |
||||
//改变是否可以全选
|
||||
changeCheckedAllStatus(); |
||||
//提示加入书架成功
|
||||
TextHelper.showText(getResources().getString(R.string.file_add_succeed, books.size())); |
||||
|
||||
} |
||||
); |
||||
|
||||
mBtnDelete.setOnClickListener( |
||||
(v) -> { |
||||
//弹出,确定删除文件吗。
|
||||
DialogCreator.createCommonDialog(this, "删除文件", "确定删除文件吗?", |
||||
true, (dialog, which) -> { |
||||
//删除选中的文件
|
||||
mCurFragment.deleteCheckedFiles(); |
||||
//提示删除文件成功
|
||||
TextHelper.showText("删除文件成功"); |
||||
}, null); |
||||
} |
||||
); |
||||
|
||||
mLocalFragment.setOnFileCheckedListener(mListener); |
||||
mCategoryFragment.setOnFileCheckedListener(mListener); |
||||
} |
||||
|
||||
@Override |
||||
protected void processLogic() { |
||||
super.processLogic(); |
||||
mCurFragment = mLocalFragment; |
||||
} |
||||
|
||||
/** |
||||
* 将文件转换成CollBook |
||||
* @param files:需要加载的文件列表 |
||||
* @return |
||||
*/ |
||||
private List<Book> convertBook(List<File> files){ |
||||
List<Book> books = new ArrayList<>(files.size()); |
||||
for(File file : files){ |
||||
//判断文件是否存在
|
||||
if (!file.exists()) continue; |
||||
|
||||
Book book = new Book(); |
||||
book.setName(file.getName().replace(".txt", "")); |
||||
book.setChapterUrl(file.getAbsolutePath()); |
||||
book.setType("本地书籍"); |
||||
book.setHistoryChapterId("未开始阅读"); |
||||
book.setNewestChapterTitle("未拆分章节"); |
||||
book.setAuthor("本地书籍"); |
||||
book.setSource(BookSource.local.toString()); |
||||
book.setDesc(""); |
||||
books.add(book); |
||||
} |
||||
return books; |
||||
} |
||||
|
||||
/** |
||||
* 改变底部选择栏的状态 |
||||
*/ |
||||
private void changeMenuStatus(){ |
||||
|
||||
//点击、删除状态的设置
|
||||
if (mCurFragment.getCheckedCount() == 0){ |
||||
mBtnAddBook.setText(getString(R.string.file_add_shelf)); |
||||
//设置某些按钮的是否可点击
|
||||
setMenuClickable(false); |
||||
|
||||
if (mCbSelectAll.isChecked()){ |
||||
mCurFragment.setChecked(false); |
||||
mCbSelectAll.setChecked(mCurFragment.isCheckedAll()); |
||||
} |
||||
|
||||
} |
||||
else { |
||||
mBtnAddBook.setText(getString(R.string.file_add_shelves, mCurFragment.getCheckedCount())); |
||||
setMenuClickable(true); |
||||
|
||||
//全选状态的设置
|
||||
|
||||
//如果选中的全部的数据,则判断为全选
|
||||
if (mCurFragment.getCheckedCount() == mCurFragment.getCheckableCount()){ |
||||
//设置为全选
|
||||
mCurFragment.setChecked(true); |
||||
mCbSelectAll.setChecked(mCurFragment.isCheckedAll()); |
||||
} |
||||
//如果曾今是全选则替换
|
||||
else if (mCurFragment.isCheckedAll()){ |
||||
mCurFragment.setChecked(false); |
||||
mCbSelectAll.setChecked(mCurFragment.isCheckedAll()); |
||||
} |
||||
} |
||||
|
||||
//重置全选的文字
|
||||
if (mCurFragment.isCheckedAll()){ |
||||
mCbSelectAll.setText("取消"); |
||||
} |
||||
else { |
||||
mCbSelectAll.setText("全选"); |
||||
} |
||||
|
||||
} |
||||
|
||||
private void setMenuClickable(boolean isClickable){ |
||||
|
||||
//设置是否可删除
|
||||
mBtnDelete.setEnabled(isClickable); |
||||
mBtnDelete.setClickable(isClickable); |
||||
|
||||
//设置是否可添加书籍
|
||||
mBtnAddBook.setEnabled(isClickable); |
||||
mBtnAddBook.setClickable(isClickable); |
||||
} |
||||
|
||||
/** |
||||
* 改变全选按钮的状态 |
||||
*/ |
||||
private void changeCheckedAllStatus(){ |
||||
//获取可选择的文件数量
|
||||
int count = mCurFragment.getCheckableCount(); |
||||
|
||||
//设置是否能够全选
|
||||
if (count > 0){ |
||||
mCbSelectAll.setClickable(true); |
||||
mCbSelectAll.setEnabled(true); |
||||
} |
||||
else { |
||||
mCbSelectAll.setClickable(false); |
||||
mCbSelectAll.setEnabled(false); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,148 @@ |
||||
package xyz.fycz.myreader.ui.filesys; |
||||
|
||||
|
||||
import xyz.fycz.myreader.base.BaseListAdapter; |
||||
import xyz.fycz.myreader.base.IViewHolder; |
||||
import xyz.fycz.myreader.greendao.service.BookService; |
||||
|
||||
import java.io.File; |
||||
import java.util.*; |
||||
|
||||
/** |
||||
* @author fengyue |
||||
* @date 2020/8/12 20:02 |
||||
*/ |
||||
|
||||
public class FileSystemAdapter extends BaseListAdapter<File> { |
||||
//记录item是否被选中的Map
|
||||
private HashMap<File, Boolean> mCheckMap = new HashMap<>(); |
||||
private int mCheckedCount = 0; |
||||
private BookService mBookService = new BookService(); |
||||
|
||||
@Override |
||||
protected IViewHolder<File> createViewHolder(int viewType) { |
||||
return new FileHolder(mCheckMap); |
||||
} |
||||
|
||||
@Override |
||||
public void refreshItems(List<File> list) { |
||||
mCheckMap.clear(); |
||||
for(File file : list){ |
||||
mCheckMap.put(file, false); |
||||
} |
||||
super.refreshItems(list); |
||||
} |
||||
|
||||
@Override |
||||
public void addItem(File value) { |
||||
mCheckMap.put(value, false); |
||||
super.addItem(value); |
||||
} |
||||
|
||||
@Override |
||||
public void addItem(int index, File value) { |
||||
mCheckMap.put(value, false); |
||||
super.addItem(index, value); |
||||
} |
||||
|
||||
@Override |
||||
public void addItems(List<File> values) { |
||||
for(File file : values){ |
||||
mCheckMap.put(file, false); |
||||
} |
||||
super.addItems(values); |
||||
} |
||||
|
||||
@Override |
||||
public void removeItem(File value) { |
||||
mCheckMap.remove(value); |
||||
super.removeItem(value); |
||||
} |
||||
|
||||
@Override |
||||
public void removeItems(List<File> value) { |
||||
//删除在HashMap中的文件
|
||||
for (File file : value){ |
||||
mCheckMap.remove(file); |
||||
//因为,能够被移除的文件,肯定是选中的
|
||||
--mCheckedCount; |
||||
} |
||||
//删除列表中的文件
|
||||
super.removeItems(value); |
||||
} |
||||
|
||||
//设置点击切换
|
||||
public void setCheckedItem(int pos){ |
||||
File file = getItem(pos); |
||||
if (isFileLoaded(file.getAbsolutePath())) return; |
||||
|
||||
boolean isSelected = mCheckMap.get(file); |
||||
if (isSelected){ |
||||
mCheckMap.put(file, false); |
||||
--mCheckedCount; |
||||
} |
||||
else{ |
||||
mCheckMap.put(file, true); |
||||
++mCheckedCount; |
||||
} |
||||
notifyDataSetChanged(); |
||||
} |
||||
|
||||
public void setCheckedAll(boolean isChecked){ |
||||
Set<Map.Entry<File, Boolean>> entrys = mCheckMap.entrySet(); |
||||
mCheckedCount = 0; |
||||
for (Map.Entry<File, Boolean> entry:entrys){ |
||||
//必须是文件,必须没有被收藏
|
||||
if (entry.getKey().isFile() && !isFileLoaded(entry.getKey().getAbsolutePath())){ |
||||
entry.setValue(isChecked); |
||||
//如果选中,则增加点击的数量
|
||||
if (isChecked){ |
||||
++mCheckedCount; |
||||
} |
||||
} |
||||
} |
||||
notifyDataSetChanged(); |
||||
} |
||||
|
||||
private boolean isFileLoaded(String path){ |
||||
//如果是已加载的文件,则点击事件无效。
|
||||
if (mBookService.findBookByPath(path) != null){ |
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
public int getCheckableCount(){ |
||||
List<File> files = getItems(); |
||||
int count = 0; |
||||
for (File file : files){ |
||||
if (!isFileLoaded(file.getAbsolutePath()) && file.isFile()) |
||||
++count; |
||||
} |
||||
return count; |
||||
} |
||||
|
||||
public boolean getItemIsChecked(int pos){ |
||||
File file = getItem(pos); |
||||
return mCheckMap.get(file); |
||||
} |
||||
|
||||
public List<File> getCheckedFiles(){ |
||||
List<File> files = new ArrayList<>(); |
||||
Set<Map.Entry<File, Boolean>> entrys = mCheckMap.entrySet(); |
||||
for (Map.Entry<File, Boolean> entry:entrys){ |
||||
if (entry.getValue()){ |
||||
files.add(entry.getKey()); |
||||
} |
||||
} |
||||
return files; |
||||
} |
||||
|
||||
public int getCheckedCount(){ |
||||
return mCheckedCount; |
||||
} |
||||
|
||||
public HashMap<File, Boolean> getCheckMap(){ |
||||
return mCheckMap; |
||||
} |
||||
} |
@ -0,0 +1,89 @@ |
||||
package xyz.fycz.myreader.ui.filesys; |
||||
|
||||
import android.media.MediaScannerConnection; |
||||
import android.os.Bundle; |
||||
|
||||
import android.os.Environment; |
||||
import androidx.recyclerview.widget.LinearLayoutManager; |
||||
import androidx.recyclerview.widget.RecyclerView; |
||||
import butterknife.BindView; |
||||
import xyz.fycz.myreader.R; |
||||
import xyz.fycz.myreader.greendao.service.BookService; |
||||
import xyz.fycz.myreader.util.media.MediaStoreHelper; |
||||
import xyz.fycz.myreader.widget.DividerItemDecoration; |
||||
import xyz.fycz.myreader.widget.RefreshLayout; |
||||
|
||||
|
||||
/** |
||||
* @author fengyue |
||||
* @date 2020/8/12 20:02 |
||||
* 本地书籍 |
||||
*/ |
||||
|
||||
public class LocalBookFragment extends BaseFileFragment { |
||||
@BindView(R.id.refresh_layout) |
||||
RefreshLayout mRlRefresh; |
||||
@BindView(R.id.local_book_rv_content) |
||||
RecyclerView mRvContent; |
||||
|
||||
@Override |
||||
protected int getContentId() { |
||||
return R.layout.fragment_local_book; |
||||
} |
||||
|
||||
@Override |
||||
protected void initWidget(Bundle savedInstanceState) { |
||||
super.initWidget(savedInstanceState); |
||||
setUpAdapter(); |
||||
} |
||||
|
||||
private void setUpAdapter() { |
||||
mAdapter = new FileSystemAdapter(); |
||||
mRvContent.setLayoutManager(new LinearLayoutManager(getContext())); |
||||
mRvContent.addItemDecoration(new DividerItemDecoration(getContext())); |
||||
mRvContent.setAdapter(mAdapter); |
||||
} |
||||
|
||||
@Override |
||||
protected void initClick() { |
||||
super.initClick(); |
||||
mAdapter.setOnItemClickListener( |
||||
(view, pos) -> { |
||||
//如果是已加载的文件,则点击事件无效。
|
||||
String path = mAdapter.getItem(pos).getAbsolutePath(); |
||||
if (BookService.getInstance().findBookByPath(path) != null) { |
||||
return; |
||||
} |
||||
|
||||
//点击选中
|
||||
mAdapter.setCheckedItem(pos); |
||||
|
||||
//反馈
|
||||
if (mListener != null) { |
||||
mListener.onItemCheckedChange(mAdapter.getItemIsChecked(pos)); |
||||
} |
||||
} |
||||
); |
||||
} |
||||
|
||||
@Override |
||||
protected void processLogic() { |
||||
super.processLogic(); |
||||
//更新媒体库
|
||||
MediaScannerConnection.scanFile(getContext(), new String[]{Environment |
||||
.getExternalStorageDirectory().getAbsolutePath()}, new String[]{"text/plain"}, null); |
||||
MediaStoreHelper.getAllBookFile(getActivity(), |
||||
(files) -> { |
||||
if (files.isEmpty()) { |
||||
mRlRefresh.showEmpty(); |
||||
} else { |
||||
mAdapter.refreshItems(files); |
||||
mRlRefresh.showFinish(); |
||||
//反馈
|
||||
if (mListener != null) { |
||||
mListener.onCategoryChanged(); |
||||
} |
||||
} |
||||
}); |
||||
} |
||||
} |
@ -0,0 +1,49 @@ |
||||
package xyz.fycz.myreader.util; |
||||
|
||||
import java.io.File; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* Created by newbiechen on 17-5-28. |
||||
*/ |
||||
|
||||
public class FileStack { |
||||
|
||||
private Node node = null; |
||||
private int count = 0; |
||||
|
||||
public void push(FileSnapshot fileSnapshot){ |
||||
if (fileSnapshot == null) return; |
||||
Node fileNode = new Node(); |
||||
fileNode.fileSnapshot = fileSnapshot; |
||||
fileNode.next = node; |
||||
node = fileNode; |
||||
++count; |
||||
} |
||||
|
||||
public FileSnapshot pop(){ |
||||
Node fileNode = node; |
||||
if (fileNode == null) return null; |
||||
FileSnapshot fileSnapshot = fileNode.fileSnapshot; |
||||
node = fileNode.next; |
||||
--count; |
||||
return fileSnapshot; |
||||
} |
||||
|
||||
public int getSize(){ |
||||
return count; |
||||
} |
||||
|
||||
//节点
|
||||
public class Node { |
||||
FileSnapshot fileSnapshot; |
||||
Node next; |
||||
} |
||||
|
||||
//文件快照
|
||||
public static class FileSnapshot{ |
||||
public String filePath; |
||||
public List<File> files; |
||||
public int scrollOffset; |
||||
} |
||||
} |
@ -0,0 +1,272 @@ |
||||
package xyz.fycz.myreader.util; |
||||
|
||||
import android.content.Context; |
||||
import android.graphics.*; |
||||
import android.graphics.drawable.BitmapDrawable; |
||||
import android.graphics.drawable.Drawable; |
||||
import xyz.fycz.myreader.R; |
||||
import xyz.fycz.myreader.application.MyApplication; |
||||
import xyz.fycz.myreader.callback.ResultCallback; |
||||
|
||||
import static xyz.fycz.myreader.util.DipPxUtil.dp2px; |
||||
|
||||
/** |
||||
* @author fengyue |
||||
* @date 2020/8/13 7:56 |
||||
*/ |
||||
public class ImageUtil { |
||||
/** |
||||
* 设置水印图片在左上角 |
||||
* @param context |
||||
* @param src |
||||
* @param watermark |
||||
* @param paddingLeft |
||||
* @param paddingTop |
||||
* @return |
||||
*/ |
||||
public static Bitmap createWaterMaskLeftTop( |
||||
Context context, Bitmap src, Bitmap watermark, |
||||
int paddingLeft, int paddingTop) { |
||||
return createWaterMaskBitmap(src, watermark, |
||||
dp2px(context, paddingLeft), dp2px(context, paddingTop)); |
||||
} |
||||
|
||||
private static Bitmap createWaterMaskBitmap(Bitmap src, Bitmap watermark, |
||||
int paddingLeft, int paddingTop) { |
||||
if (src == null) { |
||||
return null; |
||||
} |
||||
int width = src.getWidth(); |
||||
int height = src.getHeight(); |
||||
//创建一个bitmap
|
||||
Bitmap newb = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);// 创建一个新的和SRC长度宽度一样的位图
|
||||
//将该图片作为画布
|
||||
Canvas canvas = new Canvas(newb); |
||||
//在画布 0,0坐标上开始绘制原始图片
|
||||
canvas.drawBitmap(src, 0, 0, null); |
||||
//在画布上绘制水印图片
|
||||
canvas.drawBitmap(watermark, paddingLeft, paddingTop, null); |
||||
// 保存
|
||||
canvas.save(); |
||||
// 存储
|
||||
canvas.restore(); |
||||
return newb; |
||||
} |
||||
|
||||
/** |
||||
* 设置水印图片在右下角 |
||||
* @param context |
||||
* @param src |
||||
* @param watermark |
||||
* @param paddingRight |
||||
* @param paddingBottom |
||||
* @return |
||||
*/ |
||||
public static Bitmap createWaterMaskRightBottom( |
||||
Context context, Bitmap src, Bitmap watermark, |
||||
int paddingRight, int paddingBottom) { |
||||
return createWaterMaskBitmap(src, watermark, |
||||
src.getWidth() - watermark.getWidth() - dp2px(context, paddingRight), |
||||
src.getHeight() - watermark.getHeight() - dp2px(context, paddingBottom)); |
||||
} |
||||
|
||||
/** |
||||
* 设置水印图片到右上角 |
||||
* @param context |
||||
* @param src |
||||
* @param watermark |
||||
* @param paddingRight |
||||
* @param paddingTop |
||||
* @return |
||||
*/ |
||||
public static Bitmap createWaterMaskRightTop( |
||||
Context context, Bitmap src, Bitmap watermark, |
||||
int paddingRight, int paddingTop) { |
||||
return createWaterMaskBitmap( src, watermark, |
||||
src.getWidth() - watermark.getWidth() - dp2px(context, paddingRight), |
||||
dp2px(context, paddingTop)); |
||||
} |
||||
|
||||
/** |
||||
* 设置水印图片到左下角 |
||||
* @param context |
||||
* @param src |
||||
* @param watermark |
||||
* @param paddingLeft |
||||
* @param paddingBottom |
||||
* @return |
||||
*/ |
||||
public static Bitmap createWaterMaskLeftBottom( |
||||
Context context, Bitmap src, Bitmap watermark, |
||||
int paddingLeft, int paddingBottom) { |
||||
return createWaterMaskBitmap(src, watermark, dp2px(context, paddingLeft), |
||||
src.getHeight() - watermark.getHeight() - dp2px(context, paddingBottom)); |
||||
} |
||||
|
||||
/** |
||||
* 设置水印图片到中间 |
||||
* @param src |
||||
* @param src |
||||
* @param watermark |
||||
* @return |
||||
*/ |
||||
public static Bitmap createWaterMaskCenter(Bitmap src, Bitmap watermark) { |
||||
return createWaterMaskBitmap(src, watermark, |
||||
(src.getWidth() - watermark.getWidth()) / 2, |
||||
(src.getHeight() - watermark.getHeight()) / 2); |
||||
} |
||||
|
||||
/** |
||||
* 给图片添加文字到左上角 |
||||
* @param context |
||||
* @param bitmap |
||||
* @param text |
||||
* @return |
||||
*/ |
||||
public static Bitmap drawTextToLeftTop(Context context, Bitmap bitmap, String text, |
||||
int size, int color, int paddingLeft, int paddingTop) { |
||||
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); |
||||
paint.setColor(color); |
||||
paint.setTextSize(dp2px(context, size)); |
||||
Rect bounds = new Rect(); |
||||
paint.getTextBounds(text, 0, text.length(), bounds); |
||||
return drawTextToBitmap(context, bitmap, text, paint, bounds, |
||||
dp2px(context, paddingLeft), |
||||
dp2px(context, paddingTop) + bounds.height()); |
||||
} |
||||
|
||||
/** |
||||
* 绘制文字到右下角 |
||||
* @param context |
||||
* @param bitmap |
||||
* @param text |
||||
* @param size |
||||
* @param color |
||||
* @param paddingRight |
||||
* @param paddingBottom |
||||
* @return |
||||
*/ |
||||
public static Bitmap drawTextToRightBottom(Context context, Bitmap bitmap, String text, |
||||
int size, int color, int paddingRight, int paddingBottom) { |
||||
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); |
||||
paint.setColor(color); |
||||
paint.setTextSize(dp2px(context, size)); |
||||
Rect bounds = new Rect(); |
||||
paint.getTextBounds(text, 0, text.length(), bounds); |
||||
return drawTextToBitmap(context, bitmap, text, paint, bounds, |
||||
bitmap.getWidth() - bounds.width() - dp2px(context, paddingRight), |
||||
bitmap.getHeight() - dp2px(context, paddingBottom)); |
||||
} |
||||
|
||||
/** |
||||
* 绘制文字到右上方 |
||||
* @param context |
||||
* @param bitmap |
||||
* @param text |
||||
* @param size |
||||
* @param color |
||||
* @param paddingRight |
||||
* @param paddingTop |
||||
* @return |
||||
*/ |
||||
public static Bitmap drawTextToRightTop(Context context, Bitmap bitmap, String text, |
||||
int size, int color, int paddingRight, int paddingTop) { |
||||
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); |
||||
paint.setColor(color); |
||||
paint.setTextSize(dp2px(context, size)); |
||||
Rect bounds = new Rect(); |
||||
paint.getTextBounds(text, 0, text.length(), bounds); |
||||
return drawTextToBitmap(context, bitmap, text, paint, bounds, |
||||
bitmap.getWidth() - bounds.width() - dp2px(context, paddingRight), |
||||
dp2px(context, paddingTop) + bounds.height()); |
||||
} |
||||
|
||||
/** |
||||
* 绘制文字到左下方 |
||||
* @param context |
||||
* @param bitmap |
||||
* @param text |
||||
* @param size |
||||
* @param color |
||||
* @param paddingLeft |
||||
* @param paddingBottom |
||||
* @return |
||||
*/ |
||||
public static Bitmap drawTextToLeftBottom(Context context, Bitmap bitmap, String text, |
||||
int size, int color, int paddingLeft, int paddingBottom) { |
||||
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); |
||||
paint.setColor(color); |
||||
paint.setTextSize(dp2px(context, size)); |
||||
Rect bounds = new Rect(); |
||||
paint.getTextBounds(text, 0, text.length(), bounds); |
||||
return drawTextToBitmap(context, bitmap, text, paint, bounds, |
||||
dp2px(context, paddingLeft), |
||||
bitmap.getHeight() - dp2px(context, paddingBottom)); |
||||
} |
||||
|
||||
/** |
||||
* 绘制文字到中间 |
||||
* @param context |
||||
* @param bitmap |
||||
* @param text |
||||
* @param size |
||||
* @param color |
||||
* @return |
||||
*/ |
||||
public static Bitmap drawTextToCenter(Context context, Bitmap bitmap, String text, |
||||
int size, int color) { |
||||
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); |
||||
paint.setColor(color); |
||||
paint.setTextSize(dp2px(context, size)); |
||||
Rect bounds = new Rect(); |
||||
paint.getTextBounds(text, 0, text.length(), bounds); |
||||
return drawTextToBitmap(context, bitmap, text, paint, bounds, |
||||
(bitmap.getWidth() - bounds.width()) / 2, |
||||
(bitmap.getHeight() + bounds.height()) / 2); |
||||
} |
||||
|
||||
//图片上绘制文字
|
||||
private static Bitmap drawTextToBitmap(Context context, Bitmap bitmap, String text, |
||||
Paint paint, Rect bounds, int paddingLeft, int paddingTop) { |
||||
android.graphics.Bitmap.Config bitmapConfig = bitmap.getConfig(); |
||||
|
||||
paint.setDither(true); // 获取跟清晰的图像采样
|
||||
paint.setFilterBitmap(true);// 过滤一些
|
||||
// paint.setFakeBoldText(true); //加粗
|
||||
if (bitmapConfig == null) { |
||||
bitmapConfig = android.graphics.Bitmap.Config.ARGB_8888; |
||||
} |
||||
|
||||
bitmap = bitmap.copy(bitmapConfig, true); |
||||
Canvas canvas = new Canvas(bitmap); |
||||
canvas.drawText(text, paddingLeft, paddingTop, paint); |
||||
return bitmap; |
||||
} |
||||
|
||||
/** |
||||
* 缩放图片 |
||||
* @param src |
||||
* @param w |
||||
* @param h |
||||
* @return |
||||
*/ |
||||
public static Bitmap scaleWithWH(Bitmap src, double w, double h) { |
||||
if (w == 0 || h == 0 || src == null) { |
||||
return src; |
||||
} else { |
||||
// 记录src的宽高
|
||||
int width = src.getWidth(); |
||||
int height = src.getHeight(); |
||||
// 创建一个matrix容器
|
||||
Matrix matrix = new Matrix(); |
||||
// 计算缩放比例
|
||||
float scaleWidth = (float) (w / width); |
||||
float scaleHeight = (float) (h / height); |
||||
// 开始缩放
|
||||
matrix.postScale(scaleWidth, scaleHeight); |
||||
// 创建缩放后的图片
|
||||
return Bitmap.createBitmap(src, 0, 0, width, height, matrix, true); |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,71 @@ |
||||
package xyz.fycz.myreader.util; |
||||
|
||||
import android.app.Activity; |
||||
import android.content.Context; |
||||
import android.graphics.Color; |
||||
import android.os.Build; |
||||
import android.view.View; |
||||
import android.view.ViewGroup; |
||||
import android.view.Window; |
||||
import android.view.WindowManager; |
||||
import xyz.fycz.myreader.R; |
||||
|
||||
/** |
||||
* Created by newbiechen on 17-4-15. |
||||
*/ |
||||
|
||||
public class StatusBarCompat |
||||
{ |
||||
private static final int INVALID_VAL = -1; |
||||
private static final int COLOR_DEFAULT = Color.parseColor("#20000000"); |
||||
|
||||
public static void compat(Activity activity, int statusColor) |
||||
{ |
||||
//在SDK21以上,设置StatusBar的Color
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) |
||||
{ |
||||
Window window = activity.getWindow(); |
||||
//取消设置透明状态栏,使 ContentView 内容不再覆盖状态栏
|
||||
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); |
||||
//需要设置这个 flag 才能调用 setStatusBarColor 来设置状态栏颜色
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); |
||||
//设置状态栏颜色
|
||||
window.setStatusBarColor(statusColor); |
||||
} |
||||
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) |
||||
{ |
||||
int color = COLOR_DEFAULT; |
||||
ViewGroup contentView = (ViewGroup) activity.findViewById(android.R.id.content); |
||||
if (statusColor != INVALID_VAL) |
||||
{ |
||||
color = statusColor; |
||||
} |
||||
View statusBarView = activity.findViewById(R.id.status_bar); |
||||
if (statusBarView == null){ |
||||
statusBarView = new View(activity); |
||||
statusBarView.setId(R.id.status_bar); |
||||
ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, |
||||
getStatusBarHeight(activity)); |
||||
contentView.addView(statusBarView, lp); |
||||
} |
||||
statusBarView.setBackgroundColor(color); |
||||
} |
||||
} |
||||
|
||||
public static void compat(Activity activity) |
||||
{ |
||||
compat(activity, INVALID_VAL); |
||||
} |
||||
|
||||
|
||||
public static int getStatusBarHeight(Context context) |
||||
{ |
||||
int result = 0; |
||||
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android"); |
||||
if (resourceId > 0) |
||||
{ |
||||
result = context.getResources().getDimensionPixelSize(resourceId); |
||||
} |
||||
return result; |
||||
} |
||||
} |
@ -0,0 +1,30 @@ |
||||
package xyz.fycz.myreader.util.media; |
||||
|
||||
import android.content.Context; |
||||
import android.os.Bundle; |
||||
import androidx.loader.content.CursorLoader; |
||||
|
||||
/** |
||||
* Created by newbiechen on 2018/1/14. |
||||
*/ |
||||
|
||||
public class LoaderCreator { |
||||
public static final int ALL_BOOK_FILE = 1; |
||||
|
||||
public static CursorLoader create(Context context, int id, Bundle bundle) { |
||||
LocalFileLoader loader = null; |
||||
switch (id){ |
||||
case ALL_BOOK_FILE: |
||||
loader = new LocalFileLoader(context); |
||||
break; |
||||
default: |
||||
loader = null; |
||||
break; |
||||
} |
||||
if (loader != null) { |
||||
return loader; |
||||
} |
||||
|
||||
throw new IllegalArgumentException("The id of Loader is invalid!"); |
||||
} |
||||
} |
@ -0,0 +1,137 @@ |
||||
package xyz.fycz.myreader.util.media; |
||||
|
||||
import android.content.Context; |
||||
import android.database.Cursor; |
||||
import android.net.Uri; |
||||
import android.provider.MediaStore; |
||||
|
||||
import android.text.TextUtils; |
||||
import androidx.annotation.NonNull; |
||||
import androidx.loader.content.CursorLoader; |
||||
|
||||
import java.io.File; |
||||
import java.sql.Blob; |
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* Created by newbiechen on 2018/1/14. |
||||
*/ |
||||
|
||||
public class LocalFileLoader extends CursorLoader { |
||||
private static final String TAG = "LocalFileLoader"; |
||||
|
||||
private static final Uri FILE_URI = Uri.parse("content://media/external/file"); |
||||
private static final String SELECTION = MediaStore.Files.FileColumns.DATA + " like ?"; |
||||
private static final String SEARCH_TYPE = "%.txt"; |
||||
private static final String SORT_ORDER = MediaStore.Files.FileColumns.DISPLAY_NAME + " DESC"; |
||||
private static final String[] FILE_PROJECTION = { |
||||
MediaStore.Files.FileColumns.DATA, |
||||
MediaStore.Files.FileColumns.DISPLAY_NAME |
||||
}; |
||||
|
||||
public LocalFileLoader(Context context) { |
||||
super(context); |
||||
initLoader(); |
||||
} |
||||
|
||||
/** |
||||
* 为 Cursor 设置默认参数 |
||||
*/ |
||||
private void initLoader() { |
||||
setUri(FILE_URI); |
||||
setProjection(FILE_PROJECTION); |
||||
setSelection(SELECTION); |
||||
setSelectionArgs(new String[]{SEARCH_TYPE}); |
||||
setSortOrder(SORT_ORDER); |
||||
} |
||||
|
||||
public void parseData(Cursor cursor, final MediaStoreHelper.MediaResultCallback resultCallback) { |
||||
List<File> files = new ArrayList<>(); |
||||
// 判断是否存在数据
|
||||
if (cursor == null) { |
||||
// TODO:当媒体库没有数据的时候,需要做相应的处理
|
||||
// 暂时直接返回空数据
|
||||
resultCallback.onResultCallback(files); |
||||
return; |
||||
} |
||||
// 重复使用Loader时,需要重置cursor的position;
|
||||
cursor.moveToPosition(-1); |
||||
while (cursor.moveToNext()) { |
||||
String path; |
||||
|
||||
path = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DATA)); |
||||
// 路径无效
|
||||
if (TextUtils.isEmpty(path)) { |
||||
continue; |
||||
} else { |
||||
File file = new File(path); |
||||
if (file.isDirectory() || !file.exists()){ |
||||
continue; |
||||
} |
||||
else { |
||||
files.add(file); |
||||
} |
||||
} |
||||
} |
||||
if (resultCallback != null) { |
||||
resultCallback.onResultCallback(files); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 从Cursor中读取对应columnName的值 |
||||
* |
||||
* @param cursor |
||||
* @param columnName |
||||
* @param defaultValue |
||||
* @return 当columnName无效时返回默认值; |
||||
*/ |
||||
protected Object getValueFromCursor(@NonNull Cursor cursor, String columnName, Object defaultValue) { |
||||
try { |
||||
int index = cursor.getColumnIndexOrThrow(columnName); |
||||
int type = cursor.getType(index); |
||||
switch (type) { |
||||
case Cursor.FIELD_TYPE_STRING: |
||||
// TO SOLVE:某些手机的数据库将数值类型存为String类型
|
||||
String value = cursor.getString(index); |
||||
try { |
||||
if (defaultValue instanceof String) { |
||||
return value; |
||||
} else if (defaultValue instanceof Long) { |
||||
return Long.valueOf(value); |
||||
} else if (defaultValue instanceof Integer) { |
||||
return Integer.valueOf(value); |
||||
} else if (defaultValue instanceof Double) { |
||||
return Double.valueOf(value); |
||||
} else if (defaultValue instanceof Float) { |
||||
return Float.valueOf(value); |
||||
} |
||||
} catch (NumberFormatException e) { |
||||
return defaultValue; |
||||
} |
||||
case Cursor.FIELD_TYPE_INTEGER: |
||||
if (defaultValue instanceof Long) { |
||||
return cursor.getLong(index); |
||||
} else if (defaultValue instanceof Integer) { |
||||
return cursor.getInt(index); |
||||
} |
||||
case Cursor.FIELD_TYPE_FLOAT: |
||||
if (defaultValue instanceof Float) { |
||||
return cursor.getFloat(index); |
||||
} else if (defaultValue instanceof Double) { |
||||
return cursor.getDouble(index); |
||||
} |
||||
case Cursor.FIELD_TYPE_BLOB: |
||||
if (defaultValue instanceof Blob) { |
||||
return cursor.getBlob(index); |
||||
} |
||||
case Cursor.FIELD_TYPE_NULL: |
||||
default: |
||||
return defaultValue; |
||||
} |
||||
} catch (IllegalArgumentException e) { |
||||
return defaultValue; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,68 @@ |
||||
package xyz.fycz.myreader.util.media; |
||||
|
||||
import android.app.Activity; |
||||
import android.content.Context; |
||||
import android.database.Cursor; |
||||
import android.os.Bundle; |
||||
|
||||
import androidx.fragment.app.FragmentActivity; |
||||
import androidx.loader.app.LoaderManager; |
||||
import androidx.loader.content.Loader; |
||||
|
||||
import java.io.File; |
||||
import java.lang.ref.WeakReference; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* Created by newbiechen on 2018/1/14. |
||||
* 获取媒体库的数据。 |
||||
*/ |
||||
|
||||
public class MediaStoreHelper { |
||||
|
||||
/** |
||||
* 获取媒体库中所有的书籍文件 |
||||
* <p> |
||||
* 暂时只支持 TXT |
||||
* |
||||
* @param activity |
||||
* @param resultCallback |
||||
*/ |
||||
public static void getAllBookFile(FragmentActivity activity, MediaResultCallback resultCallback) { |
||||
// 将文件的获取处理交给 LoaderManager。
|
||||
activity.getSupportLoaderManager() |
||||
.initLoader(LoaderCreator.ALL_BOOK_FILE, null, new MediaLoaderCallbacks(activity, resultCallback)); |
||||
} |
||||
|
||||
public interface MediaResultCallback { |
||||
void onResultCallback(List<File> files); |
||||
} |
||||
|
||||
/** |
||||
* Loader 回调处理 |
||||
*/ |
||||
static class MediaLoaderCallbacks implements LoaderManager.LoaderCallbacks<Cursor> { |
||||
protected WeakReference<Context> mContext; |
||||
protected MediaResultCallback mResultCallback; |
||||
|
||||
public MediaLoaderCallbacks(Context context, MediaResultCallback resultCallback) { |
||||
mContext = new WeakReference<>(context); |
||||
mResultCallback = resultCallback; |
||||
} |
||||
|
||||
@Override |
||||
public Loader<Cursor> onCreateLoader(int id, Bundle args) { |
||||
return LoaderCreator.create(mContext.get(), id, args); |
||||
} |
||||
|
||||
@Override |
||||
public void onLoadFinished(Loader<Cursor> loader, Cursor data) { |
||||
LocalFileLoader localFileLoader = (LocalFileLoader) loader; |
||||
localFileLoader.parseData(data, mResultCallback); |
||||
} |
||||
|
||||
@Override |
||||
public void onLoaderReset(Loader<Cursor> loader) { |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,93 @@ |
||||
package xyz.fycz.myreader.widget; |
||||
|
||||
import android.content.Context; |
||||
import android.content.res.TypedArray; |
||||
import android.graphics.Canvas; |
||||
import android.graphics.Rect; |
||||
import android.graphics.drawable.Drawable; |
||||
|
||||
import android.view.View; |
||||
import androidx.recyclerview.widget.LinearLayoutManager; |
||||
import androidx.recyclerview.widget.RecyclerView; |
||||
|
||||
/** |
||||
* Created by newbiechen on 2017/10/8. |
||||
*/ |
||||
|
||||
public class DividerItemDecoration extends RecyclerView.ItemDecoration { |
||||
private static final String TAG = "DividerItemDecoration"; |
||||
private static final int[] ATTRS = new int[]{ |
||||
android.R.attr.listDivider |
||||
}; |
||||
|
||||
public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL; |
||||
|
||||
public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL; |
||||
|
||||
private Drawable mDrawable; |
||||
|
||||
public DividerItemDecoration(Context context) { |
||||
final TypedArray a = context.obtainStyledAttributes(ATTRS); |
||||
mDrawable = a.getDrawable(0); |
||||
a.recycle(); |
||||
} |
||||
|
||||
@Override |
||||
public void onDraw(Canvas c, RecyclerView parent) { |
||||
if (getLayoutManagerType(parent) == VERTICAL_LIST) { |
||||
drawVertical(c, parent); |
||||
} else { |
||||
drawHorizontal(c, parent); |
||||
} |
||||
} |
||||
|
||||
private int getLayoutManagerType(RecyclerView rv){ |
||||
RecyclerView.LayoutManager manager = rv.getLayoutManager(); |
||||
|
||||
if (!(manager instanceof LinearLayoutManager)){ |
||||
throw new IllegalArgumentException("only supply linearLayoutManager"); |
||||
} |
||||
return ((LinearLayoutManager) manager).getOrientation(); |
||||
} |
||||
|
||||
public void drawVertical(Canvas c, RecyclerView parent) { |
||||
final int left = parent.getPaddingLeft(); |
||||
final int right = parent.getWidth() - parent.getPaddingRight(); |
||||
|
||||
final int childCount = parent.getChildCount(); |
||||
for (int i = 0; i < childCount; i++) { |
||||
final View child = parent.getChildAt(i); |
||||
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child |
||||
.getLayoutParams(); |
||||
final int top = child.getBottom() + params.bottomMargin; |
||||
final int bottom = top + mDrawable.getIntrinsicHeight(); |
||||
mDrawable.setBounds(left, top, right, bottom); |
||||
mDrawable.draw(c); |
||||
} |
||||
} |
||||
|
||||
public void drawHorizontal(Canvas c, RecyclerView parent) { |
||||
final int top = parent.getPaddingTop(); |
||||
final int bottom = parent.getHeight() - parent.getPaddingBottom(); |
||||
|
||||
final int childCount = parent.getChildCount(); |
||||
for (int i = 0; i < childCount; i++) { |
||||
final View child = parent.getChildAt(i); |
||||
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child |
||||
.getLayoutParams(); |
||||
final int left = child.getRight() + params.rightMargin; |
||||
final int right = left + mDrawable.getIntrinsicHeight(); |
||||
mDrawable.setBounds(left, top, right, bottom); |
||||
mDrawable.draw(c); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) { |
||||
if (getLayoutManagerType(parent) == VERTICAL_LIST) { |
||||
outRect.set(0, 0, 0, mDrawable.getIntrinsicHeight()); |
||||
} else { |
||||
outRect.set(0, 0, mDrawable.getIntrinsicWidth(), 0); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,267 @@ |
||||
package xyz.fycz.myreader.widget; |
||||
|
||||
import android.content.Context; |
||||
import android.content.res.TypedArray; |
||||
import android.os.Parcel; |
||||
import android.os.Parcelable; |
||||
import android.util.AttributeSet; |
||||
import android.view.LayoutInflater; |
||||
import android.view.View; |
||||
import android.view.ViewGroup; |
||||
import android.widget.FrameLayout; |
||||
import xyz.fycz.myreader.R; |
||||
|
||||
/** |
||||
* Created by newbiechen on 17-4-22. |
||||
* 功能: |
||||
* 1. 加载动画 |
||||
* 2. 加载错误点击重新加载 |
||||
*/ |
||||
|
||||
public class RefreshLayout extends FrameLayout { |
||||
|
||||
private static final String TAG = "RefreshLayout"; |
||||
|
||||
protected static final int STATUS_LOADING = 0; |
||||
protected static final int STATUS_FINISH = 1; |
||||
protected static final int STATUS_ERROR = 2; |
||||
protected static final int STATUS_EMPTY = 3; |
||||
|
||||
private Context mContext; |
||||
|
||||
private int mEmptyViewId; |
||||
private int mErrorViewId; |
||||
private int mLoadingViewId; |
||||
|
||||
private View mEmptyView; |
||||
private View mErrorView; |
||||
private View mLoadingView; |
||||
private View mContentView; |
||||
|
||||
private OnReloadingListener mListener; |
||||
private int mStatus = 0; |
||||
|
||||
public RefreshLayout(Context context) { |
||||
this(context,null); |
||||
} |
||||
|
||||
public RefreshLayout(Context context, AttributeSet attrs) { |
||||
this(context, attrs,0); |
||||
} |
||||
|
||||
public RefreshLayout(Context context, AttributeSet attrs, int defStyleAttr) { |
||||
super(context, attrs, defStyleAttr); |
||||
mContext = context; |
||||
initAttrs(attrs); |
||||
initView(); |
||||
} |
||||
|
||||
private void initAttrs(AttributeSet attrs){ |
||||
TypedArray typedArray = mContext.obtainStyledAttributes(attrs, R.styleable.RefreshLayout); |
||||
mEmptyViewId = typedArray.getResourceId(R.styleable.RefreshLayout_layout_refresh_empty,R.layout.view_empty); |
||||
mErrorViewId = typedArray.getResourceId(R.styleable.RefreshLayout_layout_refresh_error,R.layout.view_net_error); |
||||
mLoadingViewId = typedArray.getResourceId(R.styleable.RefreshLayout_layout_refresh_loading,R.layout.view_loading); |
||||
|
||||
typedArray.recycle(); |
||||
} |
||||
|
||||
private void initView(){ |
||||
|
||||
//添加在empty、error、loading 情况下的布局
|
||||
mEmptyView = inflateView(mEmptyViewId); |
||||
mErrorView = inflateView(mErrorViewId); |
||||
mLoadingView = inflateView(mLoadingViewId); |
||||
|
||||
addView(mEmptyView); |
||||
addView(mErrorView); |
||||
addView(mLoadingView); |
||||
|
||||
//设置监听器
|
||||
mErrorView.setOnClickListener( |
||||
(view) -> { |
||||
if (mListener != null){ |
||||
toggleStatus(STATUS_LOADING); |
||||
mListener.onReload(); |
||||
} |
||||
} |
||||
); |
||||
} |
||||
|
||||
@Override |
||||
protected void onFinishInflate() { |
||||
super.onFinishInflate(); |
||||
toggleStatus(STATUS_LOADING); |
||||
} |
||||
|
||||
@Override |
||||
public void onViewAdded(View child) { |
||||
super.onViewAdded(child); |
||||
if (getChildCount() == 4){ |
||||
mContentView = child; |
||||
} |
||||
} |
||||
|
||||
//除了自带的数据,保证子类只能够添加一个子View
|
||||
@Override |
||||
public void addView(View child) { |
||||
if (getChildCount() > 4) { |
||||
throw new IllegalStateException("RefreshLayout can host only one direct child"); |
||||
} |
||||
super.addView(child); |
||||
} |
||||
|
||||
@Override |
||||
public void addView(View child, int index) { |
||||
if (getChildCount() > 4) { |
||||
throw new IllegalStateException("RefreshLayout can host only one direct child"); |
||||
} |
||||
|
||||
super.addView(child, index); |
||||
} |
||||
|
||||
@Override |
||||
public void addView(View child, ViewGroup.LayoutParams params) { |
||||
if (getChildCount() > 4) { |
||||
throw new IllegalStateException("RefreshLayout can host only one direct child"); |
||||
} |
||||
|
||||
super.addView(child, params); |
||||
} |
||||
|
||||
@Override |
||||
public void addView(View child, int index, ViewGroup.LayoutParams params) { |
||||
if (getChildCount() > 4) { |
||||
throw new IllegalStateException("RefreshLayout can host only one direct child"); |
||||
} |
||||
|
||||
super.addView(child, index, params); |
||||
} |
||||
|
||||
public void showLoading(){ |
||||
if (mStatus != STATUS_LOADING){ |
||||
toggleStatus(STATUS_LOADING); |
||||
} |
||||
} |
||||
|
||||
public void showFinish(){ |
||||
if (mStatus == STATUS_LOADING){ |
||||
toggleStatus(STATUS_FINISH); |
||||
} |
||||
} |
||||
|
||||
public void showError(){ |
||||
if (mStatus != STATUS_ERROR){ |
||||
toggleStatus(STATUS_ERROR); |
||||
} |
||||
} |
||||
|
||||
public void showEmpty(){ |
||||
if (mStatus != STATUS_EMPTY){ |
||||
toggleStatus(STATUS_EMPTY); |
||||
} |
||||
} |
||||
|
||||
//视图根据状态切换
|
||||
private void toggleStatus(int status){ |
||||
switch (status){ |
||||
case STATUS_LOADING: |
||||
mLoadingView.setVisibility(VISIBLE); |
||||
mEmptyView.setVisibility(GONE); |
||||
mErrorView.setVisibility(GONE); |
||||
if (mContentView != null){ |
||||
mContentView.setVisibility(GONE); |
||||
} |
||||
break; |
||||
case STATUS_FINISH: |
||||
if (mContentView != null){ |
||||
mContentView.setVisibility(VISIBLE); |
||||
} |
||||
mLoadingView.setVisibility(GONE); |
||||
mEmptyView.setVisibility(GONE); |
||||
mErrorView.setVisibility(GONE); |
||||
break; |
||||
case STATUS_ERROR: |
||||
mErrorView.setVisibility(VISIBLE); |
||||
mLoadingView.setVisibility(GONE); |
||||
mEmptyView.setVisibility(GONE); |
||||
if (mContentView != null){ |
||||
mContentView.setVisibility(GONE); |
||||
} |
||||
break; |
||||
case STATUS_EMPTY: |
||||
mEmptyView.setVisibility(VISIBLE); |
||||
mErrorView.setVisibility(GONE); |
||||
mLoadingView.setVisibility(GONE); |
||||
if (mContentView != null){ |
||||
mContentView.setVisibility(GONE); |
||||
} |
||||
break; |
||||
} |
||||
mStatus = status; |
||||
} |
||||
|
||||
protected int getStatus(){ |
||||
return mStatus; |
||||
} |
||||
|
||||
public void setOnReloadingListener(OnReloadingListener listener){ |
||||
mListener = listener; |
||||
} |
||||
|
||||
private View inflateView(int id){ |
||||
return LayoutInflater.from(mContext) |
||||
.inflate(id,this,false); |
||||
} |
||||
|
||||
//数据存储
|
||||
@Override |
||||
protected Parcelable onSaveInstanceState() { |
||||
Parcelable superParcel = super.onSaveInstanceState(); |
||||
SavedState savedState = new SavedState(superParcel); |
||||
savedState.status = mStatus; |
||||
return savedState; |
||||
} |
||||
|
||||
@Override |
||||
protected void onRestoreInstanceState(Parcelable state) { |
||||
SavedState savedState = (SavedState) state; |
||||
super.onRestoreInstanceState(savedState.getSuperState()); |
||||
//刷新状态
|
||||
toggleStatus(savedState.status); |
||||
} |
||||
|
||||
static class SavedState extends BaseSavedState { |
||||
int status; |
||||
|
||||
SavedState(Parcelable superState) { |
||||
super(superState); |
||||
} |
||||
|
||||
private SavedState(Parcel in) { |
||||
super(in); |
||||
status = in.readInt(); |
||||
} |
||||
|
||||
@Override |
||||
public void writeToParcel(Parcel out, int flags) { |
||||
super.writeToParcel(out, flags); |
||||
out.writeInt(status); |
||||
} |
||||
|
||||
public static final Creator<SavedState> CREATOR |
||||
= new Creator<SavedState>() { |
||||
public SavedState createFromParcel(Parcel in) { |
||||
return new SavedState(in); |
||||
} |
||||
|
||||
public SavedState[] newArray(int size) { |
||||
return new SavedState[size]; |
||||
} |
||||
}; |
||||
} |
||||
|
||||
//添加错误重新加载的监听
|
||||
public interface OnReloadingListener{ |
||||
void onReload(); |
||||
} |
||||
} |
@ -0,0 +1,6 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android"> |
||||
<item android:color="@color/gray" android:state_enabled="false" /> |
||||
<item android:color="@color/white" android:state_pressed="true" /> |
||||
<item android:color="@color/colorAccent"/> |
||||
</selector> |
@ -0,0 +1,6 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android"> |
||||
<item android:color="@color/gray" android:state_enabled="false" /> |
||||
<item android:color="@color/white" android:state_pressed="true" /> |
||||
<item android:color="@color/black"/> |
||||
</selector> |
@ -0,0 +1,5 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android"> |
||||
<item android:color="@color/gray" android:state_enabled="false" /> |
||||
<item android:color="@color/black" /> |
||||
</selector> |
After Width: | Height: | Size: 616 B |
After Width: | Height: | Size: 686 B |
After Width: | Height: | Size: 696 B |
After Width: | Height: | Size: 1007 B |
After Width: | Height: | Size: 5.5 KiB |
After Width: | Height: | Size: 205 B |
@ -0,0 +1,14 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" |
||||
android:width="24dp" |
||||
android:height="24dp" |
||||
android:viewportHeight="680.31" |
||||
android:viewportWidth="680.31"> |
||||
|
||||
<path |
||||
android:fillColor="#2C2C2C" |
||||
android:pathData="M339.865,79.038c-147.014,0-266.518,119.198-266.518,266.519 c0,147.011,119.198,266.519,266.518,266.519c147.013,0,266.52-119.201,266.52-266.519 C606.078,198.542,486.878,79.038,339.865,79.038L339.865,79.038z M339.865,587.929c-133.871,0-242.373-108.505-242.373-242.373 c0-133.872,108.502-242.066,242.373-242.066c133.868,0,242.372,108.5,242.372,242.373 C582.237,479.731,473.432,587.929,339.865,587.929L339.865,587.929z M339.865,587.929" /> |
||||
<path |
||||
android:fillColor="#2C2C2C" |
||||
android:pathData="M305.329,491.346c20.782,0,28.118,33.619,28.118,33.619h12.836c0,0,7.337-33.619,28.12-33.619h133.869 c7.95,0,14.673-6.725,14.673-14.671V242.25c0-33.316-33.316-33.316-33.316-33.316H361.261c-10.697,0-21.396,13.449-21.396,20.479 c0-7.03-10.391-20.479-21.088-20.479H190.103c0,0-33.317,0-33.317,33.316v234.426c0,8.253,6.418,14.671,14.67,14.671H305.329z M348.117,236.75c0-6.115,4.89-11.31,11.005-11.31h139.065c3.973,0,7.336,3.361,7.336,7.64l0.607,235.647 c0,4.279-3.36,7.644-7.333,7.644H372.873c-14.06,0-24.756,15.589-24.756,15.589V236.75z M173.902,233.08 c0-4.279,3.364-7.64,7.336-7.64h139.066c6.112,0,11.003,4.891,11.003,11.31v254.903c0,0-11.003-15.589-24.759-15.589H180.627 c-3.974,0-7.337-3.36-7.337-7.639L173.902,233.08z M173.902,233.08" /> |
||||
</vector> |
@ -0,0 +1,10 @@ |
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" |
||||
android:width="24dp" |
||||
android:height="24dp" |
||||
android:tint="#707070" |
||||
android:viewportHeight="24.0" |
||||
android:viewportWidth="24.0"> |
||||
<path |
||||
android:fillColor="#FF000000" |
||||
android:pathData="M10,4H4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V8c0,-1.1 -0.9,-2 -2,-2h-8l-2,-2z" /> |
||||
</vector> |
@ -0,0 +1,5 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"> |
||||
<corners android:radius="3dp"/> |
||||
<solid android:color="@color/light_blue"/> |
||||
</shape> |
@ -0,0 +1,6 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android"> |
||||
<item android:drawable="@drawable/shape_blue" android:state_pressed="true"/> |
||||
<item android:drawable="@drawable/shape_unclick" android:state_enabled="false"/> |
||||
<item android:drawable="@drawable/shape_corner_blue"/> |
||||
</selector> |
@ -0,0 +1,6 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android"> |
||||
<item android:drawable="@drawable/shape_blue" android:state_pressed="true"/> |
||||
<item android:drawable="@drawable/shape_unclick" android:state_enabled="false"/> |
||||
<item android:drawable="@drawable/shape_corner_black" /> |
||||
</selector> |
@ -0,0 +1,6 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android"> |
||||
<item android:drawable="@drawable/ic_cb_file_selected" android:state_pressed="true"/> |
||||
<item android:drawable="@drawable/ic_cb_file_selected" android:state_checked="true"/> |
||||
<item android:drawable="@drawable/ic_cb_file_normal"/> |
||||
</selector> |
@ -0,0 +1,10 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android"> |
||||
<item android:state_pressed="true" |
||||
android:drawable="@color/widget_button_selected"/> |
||||
<item android:state_selected="true" |
||||
android:drawable="@color/widget_button_selected"/> |
||||
|
||||
<item |
||||
android:drawable="@color/widget_button_normal"/> |
||||
</selector> |
@ -0,0 +1,6 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android"> |
||||
<item android:drawable="@mipmap/pwd_visiable" android:state_checked="true"/> |
||||
<item android:drawable="@mipmap/pwd_gone" android:state_checked="false"/> |
||||
<item android:drawable="@mipmap/pwd_gone" /> |
||||
</selector> |
@ -0,0 +1,6 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" |
||||
android:shape="rectangle"> |
||||
<corners android:radius="3dp"/> |
||||
<solid android:color="@color/colorAccent" /> |
||||
</shape> |
@ -0,0 +1,6 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" |
||||
android:shape="rectangle"> |
||||
<stroke android:color="@color/black" android:width="1dp"/> |
||||
<corners android:radius="3dp"/> |
||||
</shape> |
@ -0,0 +1,6 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" |
||||
android:shape="rectangle"> |
||||
<stroke android:color="@color/colorAccent" android:width="1dp"/> |
||||
<corners android:radius="3dp"/> |
||||
</shape> |
@ -0,0 +1,6 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" |
||||
android:shape="rectangle"> |
||||
<stroke android:color="@color/gray" android:width="1dp"/> |
||||
<corners android:radius="3dp" /> |
||||
</shape> |
@ -0,0 +1,60 @@ |
||||
<?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"> |
||||
<!--指示器--> |
||||
<include layout="@layout/scroll_title_indicator"/> |
||||
|
||||
<androidx.viewpager.widget.ViewPager |
||||
android:id="@+id/tab_vp" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="0dp" |
||||
android:layout_weight="1"> |
||||
</androidx.viewpager.widget.ViewPager> |
||||
|
||||
<!--文件点击按钮--> |
||||
<RelativeLayout |
||||
android:layout_width="match_parent" |
||||
android:layout_height="52dp" |
||||
android:paddingRight="10dp" |
||||
android:gravity="center_vertical"> |
||||
|
||||
|
||||
<CheckBox |
||||
android:id="@+id/file_system_cb_selected_all" |
||||
android:layout_width="120dp" |
||||
android:layout_height="40dp" |
||||
android:layout_centerVertical="true" |
||||
android:layout_marginStart="15dp" |
||||
android:text="全选" |
||||
android:textSize="15dp" |
||||
android:textColor="@color/selector_cb_file" |
||||
android:theme="@style/MyCheckBox" /> |
||||
|
||||
<Button |
||||
android:id="@+id/file_system_btn_add_book" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="35dp" |
||||
android:minWidth="110dp" |
||||
android:layout_alignParentRight="true" |
||||
android:layout_centerVertical="true" |
||||
android:background="@drawable/selector_btn_add" |
||||
android:textColor="@color/selector_btn_file_add" |
||||
android:text="加入书架" /> |
||||
|
||||
<Button |
||||
android:id="@+id/file_system_btn_delete" |
||||
android:layout_width="70dp" |
||||
android:layout_height="35dp" |
||||
android:layout_centerVertical="true" |
||||
android:layout_marginRight="15dp" |
||||
android:background="@drawable/selector_btn_add" |
||||
android:textColor="@color/selector_btn_file_add" |
||||
android:layout_toLeftOf="@id/file_system_btn_add_book" |
||||
android:text="删除"/> |
||||
</RelativeLayout> |
||||
</LinearLayout> |
@ -0,0 +1,48 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||
xmlns:tools="http://schemas.android.com/tools" |
||||
android:orientation="vertical" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="match_parent"> |
||||
<!--path--> |
||||
<LinearLayout |
||||
android:layout_width="match_parent" |
||||
android:layout_height="35dp" |
||||
android:orientation="horizontal"> |
||||
<TextView |
||||
style="@style/SingleLine" |
||||
android:id="@+id/file_category_tv_path" |
||||
android:layout_width="0dp" |
||||
android:layout_height="match_parent" |
||||
android:layout_weight="1" |
||||
android:gravity="center_vertical" |
||||
android:padding="10dp" |
||||
android:textSize="12dp" |
||||
android:textColor="@color/text_common_h2" |
||||
tools:text="存储卡:/"/> |
||||
|
||||
<View |
||||
android:layout_width="2dp" |
||||
android:layout_height="match_parent" |
||||
android:layout_marginTop="5dp" |
||||
android:layout_marginBottom="5dp" |
||||
android:background="@color/divider_narrow"/> |
||||
|
||||
<TextView |
||||
android:id="@+id/file_category_tv_back_last" |
||||
android:layout_width="80dp" |
||||
android:layout_height="match_parent" |
||||
android:gravity="center" |
||||
android:text="上一级" |
||||
android:textSize="12dp" |
||||
android:drawableLeft="@drawable/ic_back_last" |
||||
android:textColor="@color/text_common_h2"/> |
||||
</LinearLayout> |
||||
|
||||
<androidx.recyclerview.widget.RecyclerView |
||||
android:id="@+id/file_category_rv_content" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="0dp" |
||||
android:layout_weight="1"> |
||||
</androidx.recyclerview.widget.RecyclerView> |
||||
</LinearLayout> |
@ -0,0 +1,12 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<xyz.fycz.myreader.widget.RefreshLayout |
||||
xmlns:android="http://schemas.android.com/apk/res/android" |
||||
android:id="@+id/refresh_layout" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="match_parent"> |
||||
<androidx.recyclerview.widget.RecyclerView |
||||
android:id="@+id/local_book_rv_content" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="match_parent"> |
||||
</androidx.recyclerview.widget.RecyclerView> |
||||
</xyz.fycz.myreader.widget.RefreshLayout> |
@ -0,0 +1,91 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||
xmlns:tools="http://schemas.android.com/tools" |
||||
android:orientation="horizontal" |
||||
android:layout_width="match_parent" |
||||
android:background="@drawable/selector_common_bg" |
||||
android:layout_height="60dp"> |
||||
|
||||
<FrameLayout |
||||
android:id="@+id/file_fl_icon" |
||||
android:layout_width="60dp" |
||||
android:layout_height="match_parent"> |
||||
<!--文件夹标识或已选文件标识--> |
||||
<ImageView |
||||
android:id="@+id/file_iv_icon" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="match_parent" |
||||
android:scaleType="center" |
||||
android:visibility="gone" |
||||
tools:src="@drawable/ic_folder"/> |
||||
<!--选择是否添加文件--> |
||||
<CheckBox |
||||
android:id="@+id/file_cb_select" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:layout_gravity="center" |
||||
android:enabled="true" |
||||
android:clickable="false" |
||||
android:focusable="true" |
||||
android:theme="@style/MyCheckBox" /> |
||||
</FrameLayout> |
||||
|
||||
<RelativeLayout |
||||
android:layout_width="0dp" |
||||
android:layout_height="match_parent" |
||||
android:layout_weight="1" |
||||
android:paddingTop="8dp" |
||||
android:paddingBottom="8dp"> |
||||
|
||||
<TextView |
||||
style="@style/SingleLine" |
||||
android:id="@+id/file_tv_name" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:textSize="16sp" |
||||
android:textColor="@color/title_black" |
||||
android:text="英语四级"/> |
||||
|
||||
<LinearLayout |
||||
android:id="@+id/file_ll_brief" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:orientation="horizontal" |
||||
android:gravity="center_vertical" |
||||
android:layout_alignParentBottom="true"> |
||||
|
||||
<TextView |
||||
style="@style/File_Tag" |
||||
android:id="@+id/file_tv_tag" |
||||
android:layout_toRightOf="@+id/file_fl_icon" |
||||
android:layout_alignParentBottom="true" |
||||
android:layout_marginRight="15dp" |
||||
android:text="TXT" |
||||
android:background="@drawable/ic_tag_txt"/> |
||||
|
||||
<TextView |
||||
android:id="@+id/file_tv_size" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:textColor="@color/title_black" |
||||
android:layout_marginRight="15dp" |
||||
tools:text="324kb"/> |
||||
|
||||
<TextView |
||||
android:id="@+id/file_tv_date" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:textColor="@color/title_black" |
||||
tools:text="2017-05-22"/> |
||||
</LinearLayout> |
||||
|
||||
<TextView |
||||
android:id="@+id/file_tv_sub_count" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:layout_alignParentBottom="true" |
||||
tools:text="0 项" |
||||
android:textColor="@color/title_black" |
||||
android:visibility="gone"/> |
||||
</RelativeLayout> |
||||
</LinearLayout> |
@ -0,0 +1,28 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android" |
||||
xmlns:app="http://schemas.android.com/apk/res-auto"> |
||||
<com.google.android.material.appbar.AppBarLayout |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content"> |
||||
|
||||
<androidx.appcompat.widget.Toolbar |
||||
xmlns:android="http://schemas.android.com/apk/res/android" |
||||
android:id="@+id/toolbar" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="?attr/actionBarSize" |
||||
android:background="@color/white" |
||||
android:theme="@style/Theme.ToolBar.Menu" |
||||
app:layout_scrollFlags="scroll|enterAlways"> |
||||
</androidx.appcompat.widget.Toolbar> |
||||
|
||||
<com.google.android.material.tabs.TabLayout |
||||
android:id="@+id/tab_tl_indicator" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="40dp" |
||||
android:background="@color/white" |
||||
app:tabTextColor="@color/title_black" |
||||
app:tabSelectedTextColor="@color/colorAccent" |
||||
app:tabIndicatorColor="@color/colorAccent"> |
||||
</com.google.android.material.tabs.TabLayout> |
||||
</com.google.android.material.appbar.AppBarLayout> |
||||
</merge> |
@ -0,0 +1,44 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<!-- |
||||
~ Copyright (c) 2016 JustWayward Team |
||||
~ |
||||
~ Licensed under the Apache License, Version 2.0 (the "License"); |
||||
~ you may not use this file except in compliance with the License. |
||||
~ You may obtain a copy of the License at |
||||
~ |
||||
~ http://www.apache.org/licenses/LICENSE-2.0 |
||||
~ |
||||
~ Unless required by applicable law or agreed to in writing, software |
||||
~ distributed under the License is distributed on an "AS IS" BASIS, |
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
~ See the License for the specific language governing permissions and |
||||
~ limitations under the License. |
||||
--> |
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||
android:id="@+id/rl_empty_view" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="match_parent" |
||||
android:layout_centerInParent="true" |
||||
android:orientation="vertical"> |
||||
|
||||
<LinearLayout |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:layout_centerInParent="true" |
||||
android:orientation="vertical"> |
||||
|
||||
<ImageView |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:layout_gravity="center_horizontal" |
||||
android:background="@drawable/ic_no_data" /> |
||||
|
||||
<TextView |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:layout_gravity="center_horizontal" |
||||
android:layout_marginTop="10dp" |
||||
android:text="暂无数据" |
||||
android:textColor="@color/text_common_h1" /> |
||||
</LinearLayout> |
||||
</RelativeLayout> |
@ -0,0 +1,11 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="match_parent"> |
||||
|
||||
<ProgressBar |
||||
android:id="@+id/loadding_pb" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:layout_centerInParent="true"/> |
||||
</RelativeLayout> |
@ -0,0 +1,31 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||
android:id="@+id/rl_empty_view" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="match_parent" |
||||
android:layout_centerInParent="true" |
||||
android:orientation="vertical"> |
||||
|
||||
<LinearLayout |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:layout_centerInParent="true" |
||||
android:orientation="vertical"> |
||||
|
||||
<ImageView |
||||
android:id="@+id/book_shelf_iv_empty" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:layout_gravity="center_horizontal" |
||||
android:background="@drawable/ic_no_data" /> |
||||
|
||||
<TextView |
||||
android:id="@+id/tvEmptyView" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:layout_gravity="center_horizontal" |
||||
android:layout_marginTop="10dp" |
||||
android:text="网络连接失败..." |
||||
android:textColor="@color/text_common_h2" /> |
||||
</LinearLayout> |
||||
</RelativeLayout> |
After Width: | Height: | Size: 63 KiB |
After Width: | Height: | Size: 920 B |
After Width: | Height: | Size: 1.4 KiB |
@ -1,2 +1,2 @@ |
||||
#Fri Aug 07 09:42:27 CST 2020 |
||||
VERSION_CODE=140 |
||||
#Wed Aug 12 21:27:50 CST 2020 |
||||
VERSION_CODE=141 |
||||
|