@ -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 |
#Wed Aug 12 21:27:50 CST 2020 |
||||||
VERSION_CODE=140 |
VERSION_CODE=141 |
||||||
|