- 页签使用PagerTab。使用PagerTab时,setOnPageChangeListener需要绑定在pageTab上。否则会出现滑动异常。(why?)
2.在ViewPager中添加Fragment,使用FragmentPagerAdapter。getSupportFragmentManager()只有在FragmentActivity中才可调用,AppCompatActivity是继承于FragmentActivity的。
3.使用string.xml中的数组时,使用代码
context.getResources().getStringArray(R.array.arrayname);
4.使用工厂模式,创建Fragment。
通过position去创建Fragment,创建好的fragment放入Map中,要使用的时候不需要重新创建,只需要拿出来使用即可。代码如下:
//通过position创建Fragment,然后将创建的Fragment装到Map中。
private static ArrayMap<Integer,BaseFragment> map = new ArrayMap<>();
public static BaseFragment createFragment(int position){
BaseFragment baseFragment = map.get(position);
if(baseFragment==null){
switch (position){
case 0:
baseFragment = new HomeFragment();
break;
case 1:
baseFragment = new AppFragment();
break;
case 2:
baseFragment = new GameFragment();
break;
case 3:
baseFragment = new SubjectFragment();
break;
case 4:
baseFragment = new RecommendFragment();
break;
case 5:
baseFragment = new CategoryFragment();
break;
case 6:
baseFragment = new HotFragment();
break;
}
map.put(position,baseFragment);
}
return baseFragment;
}
5.主题问题。
如果项目的BaseActivity继承了AppCompatActivity,那么项目的主题一定要是Theme.AppCompat的。否则就会崩溃。
6.在BaseFragment中有这样的逻辑,根据网络情况显示不同的页面,加载失败,数据为空,加载中,加载成功,这几种情况显示的页面不同。
思路: 创建一个FrameLayout,命名为LoadingView,往这个LoadingView中添加4个页面, 分别是errorView,loadingView,emptyView,successView,根据网络情况的不同显示不同的页面。successView的情况特殊一点,为了避免资源浪费,就是只有在加载成功情况下才会去创建successView。而每个子类加载成功的页面不同,所以createSuccessView()要写成抽象方法,供子类重写。
在BaseFragment中使用LoadingView:
public abstract class BaseFragment extends Fragment {
private LoadingView loadingView;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
loadingView = new LoadingView(UIUtils.getContext()){
@Override
public View createSuccessView() {
return fragmentCreateSuccessView();
}
};
return loadingView;
}
public abstract View fragmentCreateSuccessView();
}
其中抽象方法:fragmentCreateSuccessView()是供子类重写的。
7.自定义进度条。
两张图片叠加使用drawable文件的根节点是:layer-list ,再在其中添加动画。使用,在ProgressBar中添加属性:android:indeterminateDrawable 将drawable文件赋值给该属性即可。
7.1 设置动画属性文件
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!--此标签表示几张图片叠加-->
<!--第一张图片-->
<item>
<rotate
android:drawable="@drawable/spinner_big_inner"
android:fromDegrees="0"
android:toDegrees="720"
android:pivotY="50%"
android:pivotX="50%"/>
</item>
<!--第二张图片-->
<item>
<rotate
android:drawable="@drawable/spinner_big_outer"
android:fromDegrees="720"
android:toDegrees="0"
android:pivotX="50%"
android:pivotY="50%"/>
</item>
</layer-list>
7.2 使用
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:indeterminateDrawable="@drawable/custom_loading"/>
8.获取当前类名
getClass().getSimpleName();
9.ListView的封装
9.1 封装BaseHolder
/**
* Created by kimliu on 2018/5/27.
* ListView 中 ViewHolder的父类
*
* 在ListView的Adapter中 getView()中要使用到ViewHolder
* 而ViewHolder的作用,是用来
* 1.初始化Item 布局文件
* 2.findViewById 找到布局文件中的控件
* 3.打一个tag
* 4.根据数据,更新UI 也就是把数据填充到UI中
*
* 因此 封装的BaseHolder 就必须完成这四步,无法确定的 比如初始化Item布局文件,
* 就写一个抽象方法,交给子类去做
*/
public abstract class BaseHolder<T> {
private View mRootView;
private T data;//Item对应的数据
/**
* 在构造方法中就initView
*/
public BaseHolder(){
mRootView = initView();
mRootView.setTag(this);//3.给View设置Tag
}
/**
* 1.初始化Item布局文件
* 2. findViewById 找到布局中的控件
* 这两步父类无法实现,需要交给子类去实现
* @return
*/
public abstract View initView();
/**
* 外界拿到布局对象的方法
*/
public View getRootView(){
return mRootView;
}
/**
* 4.刷新界面,将数据设置给控件
* 这个方法在各个子类中的实现也各不相同,因此也需要交由子类去完成
*/
public abstract void refreshView(T data);
/**
* 外界将数据传递进来的方法
*/
public void setData(T data){
this.data = data;
//一拿到data 立即用上
refreshView(data);
}
public T getData(){
return data;
}
}
9.2 封装BaseAdapter
/**
* Created by kimliu on 2018/5/27.
* ListView 的 Adapter 的封装
*
*/
public abstract class MyBaseAdapter<T> extends BaseAdapter {
private ArrayList<T> data;
public void setData(ArrayList<T> data){
this.data = data;
}
@Override
public int getCount() {
return data.size();
}
@Override
public T getItem(int position) {
return data.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
BaseHolder holder ;
if(convertView == null){
//此时如果添加new 出一个需要的Holder 那么 initView 和 findViewById 都完成了
//但是我们并不知道需要的Holder是什么类型的,因此可以交给子类完成
//创建抽象方法,由子类重写
holder = getHolder();
}else {
holder = (BaseHolder) convertView.getTag();
}
holder.setData(getItem(position));//设置数据
return holder.getRootView();
}
public abstract BaseHolder<T> getHolder();
}
9.3 封装之后的使用,只需要new 出需要的Holder 继承BaseHolder 在子类的initView中进行 inflate 和 findViewById的操作,在refreshView中进行data的赋值即可。
如:Adapter
class HomeAdapter extends MyBaseAdapter<String>{
@Override
public BaseHolder<String> getHolder() {
return new HomeHolder();
}
}
ViewHolder:
/**
* Created by kimliu on 2018/5/27.
*/
public class HomeHolder extends BaseHolder<String> {
private TextView textView;
@Override
public View initView() {
textView = new TextView(UIUtils.getContext());
return textView;
}
@Override
public void refreshView(String data) {
textView.setText(data);
}
}
这是最基础的ListView的封装。
10.封装加载更多ListView。
思路:展示两种布局,第一种 普通布局,第二种,加载更多布局。加载更多布局是在position == getCount()-1时显示。
10.1.在MyBaseAdapter中添加两个方法,这两个方法是ListView显示不同布局必须要重写的两个方法:
/**
* 返回要加载的布局类型,有几种就返回几种
* @param position
* @return
*/
@Override
public int getItemViewType(int position) {
if(position == getCount()-1){
//最后一个 加载更多类型
return TYPE_MORE;
}else{
return getType();
}
}
/**
* 子类可能不止有普通类型 还有可能有别的类型,这里提供一个可以更改的入口
*
* 不写成抽象方法,子类如果有更多的类型就重写,没有就直接返回普通类型
* @return
*/
public int getType(){
return TYPE_NOMAL;
}
/**
* 将来子类也可重写该方法 返回有几种type
* @return
*/
@Override
public int getViewTypeCount() {
return 2;
}
10.2. 在getView()中 根据Type设置布局,当type为TYPE_MORE时,设置加载更多布局,其它设置普通布局。
if(getItemViewType(position) == TYPE_MORE){
//是加载更多类型
holder = new MoreHolder();
}else{
//此时如果添加new 出一个需要的Holder 那么 initView 和 findViewById 都完成了
//但是我们并不知道需要的Holder是什么类型的,因此可以交给子类完成
//创建抽象方法,由子类重写
holder = getHolder();
}
10.3. 加载更多布局,要根据状态去刷新界面。
加载更多有几种状态:1.可以加载更多,2.加载失败 3.不需要加载更多 根据这几种状态去刷新界面。
可以在MoreHolder的构造方法中传入一个boolean类型的值,如果为true 则为可以加载更多 如果为false 则为不需要加载更多的状态 那么就可以setData(int) 在MoreHolder中setData,设置的是int类型的状态,因为BaseHolder是根据传入的值去刷新界面的,而MoreHolder是根据状态刷新界面,所以这里MoreHolder的泛型就为Integer。
public class MoreHolder extends BaseHolder<Integer> {
public static final int LOAD_MORE_MORE = 0;
public static final int LOAD_MORE_ERROR = 1;
public static final int LOAD_MORE_NONE = 2;
private LinearLayout ll_more;
private TextView tv_error;
public MoreHolder(boolean hasMore) {
setData(hasMore?LOAD_MORE_MORE:LOAD_MORE_NONE);
}
@Override
public View initView() {
View view = KimliuUtils.inflate(UIUtils.getContext(), R.layout.item_more);
ll_more = view.findViewById(R.id.ll_more);
tv_error = view.findViewById(R.id.tv_error);
return view;
}
@Override
public void refreshView(Integer data) {
switch (data){
case LOAD_MORE_MORE:
//加载更多
ll_more.setVisibility(View.VISIBLE);
tv_error.setVisibility(View.GONE);
break;
case LOAD_MORE_ERROR:
//加载失败
ll_more.setVisibility(View.GONE);
tv_error.setVisibility(View.VISIBLE);
break;
case LOAD_MORE_NONE:
//不需要加载更多
ll_more.setVisibility(View.GONE);
tv_error.setVisibility(View.GONE);
break;
}
}
10.4.界面已经写完了,现在就要开始加载数据了。
10.4.1 在MyBaseAdapter中写一个方法 onLoadMore()加载更多,具体代码中已注释
private boolean isLoadMore = false;
public void onLoadMore(final MoreHolder moreHolder){
if(!isLoadMore){
//只有当isLoadMore为false才会进来
new Thread(){
@Override
public void run() {
isLoadMore = true;
//开启一个子线程,进行数据的加载
moreList = loadMore();
UIUtils.runOnUIThread(new Runnable() {
@Override
public void run() {
//根据moreList去刷新界面,刷新界面需要在主线程中去完成
if(moreList!=null){
if(moreList.size() < limtNum()){
//每一页为limtNum条数据,如果 < limtNum()了,说明到最后一页了
moreHolder.setData(MoreHolder.LOAD_MORE_NONE);
Toast.makeText(UIUtils.getContext(),
"没有更多数据了", Toast.LENGTH_SHORT)
.show();
}else {
// 还有更多数据
moreHolder.setData(MoreHolder.LOAD_MORE_MORE);
}
//根据加载更多的结果去刷新页面
// 将更多数据追加到当前集合中
data.addAll(moreList);
// 刷新界面
MyBaseAdapter.this.notifyDataSetChanged();
}else{
//如果moreList 为空,说明加载失败
moreHolder.setData(MoreHolder.LOAD_MORE_ERROR);
}
//加载完毕,将isLoadMore改为false
isLoadMore = false;
}
});
}
}.start();
}
}
/**
* 如果每页显示的条数不为20 那么子类重写
* @return
*/
public int limtNum(){
return 20;
}
/**
* 每个子类加载更多的情况不同,所有交由子类重写
* @return
*/
public abstract ArrayList<T> loadMore();
加载更多的方法已经写好,我们在什么时候调用呢?
10.4.2 调用加载更多的方法,我们需要在加载更多布局显示的时候调用onLoadMore(),对ListView来说,item显示的时候,一定会调用getView方法,在getView方法中判断Type 如果Type == TYPE_MORE,说明此时需要加载更多,同时我们需要进一步的判断,加载更多布局此时的状态,如果状态为LOAD_MORE_MORE,才会去加载更多。
if(getItemViewType(position) == TYPE_NOMAL){
holder.setData(getItem(position));//设置数据
}else{
//当ListView的Item显示时,必定会调getView方法,
//因此,当getItemViewType(position) == TYPE_MORE
//时,也就是加载更多布局显示的时候,我们需要调用//
//onLoadMore(),而此时的holder 正是moreHolder
MoreHolder moreHolder = (MoreHolder) holder;
if(moreHolder.getData() == MoreHolder.LOAD_MORE_MORE){
//当moreHolder状态为可以加载更多时,才去加载更多
onLoadMore(moreHolder);
}
}