I haven't written a blog for a long time, mainly because I had to work hard to complete an outsourcing project before, and because it was the first time to outsource, a lot of things had to be dealt with, of course, I also learned a lot. In this series, I will share some of the outsourcing code with you and learn together.
Project description: 1. This is a simple and easy-to-use network request encapsulation project, which can be quickly transplanted to realize the development of Android network layer; 2. This is a common app template, which uses a better app architecture and achieves a series of optimization.
Let's look at some pictures first.
As you can see from the two pages of the example, we often encounter such styles and requests in android application development.
Okay, no more nonsense. Let's start with the text.
The article structure: (1) the architecture pattern of the whole application; (2) the encapsulation of Activity and Fragment;
1. Architectural model of the whole application:
Mainly MVC mode. Students who do not understand MVC, please see MVC Mode in Android . For our project, the M layer is my bean package, the C layer is acted by activity, and the V layer is acted by custom view and xml file. Controller (controller) is an intermediate bridge. It works with View (view) and Model (model) through interface communication, and plays a communication role between them.
Encapsulation of Activity and Fragment:
As you can see, I encapsulate three levels of abstract activities, each responsible for different businesses. This is a very common activity encapsulation for app s.
1.BaseActivity is the highest parent class in app design. It handles some load optimization, uses template pattern, develops a series of abstract methods, and manages the related app's overall functions (such as withdrawing from the whole app).
public abstract class BaseActivity extends AppCompatActivity {
private static List<Activity> activities = new ArrayList<>();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Logger.i("Activity--->" + getClass().getSimpleName());
addActivity(this);
if (getLayoutId() != 0) {
setContentView(getLayoutId());
} else {
throw new IllegalArgumentException("Return a correct ContentView");
}
ButterKnife.bind(this);
initView();
initEvent();
loadData();
}
protected abstract int getLayoutId();
protected abstract void initView();
protected abstract void initEvent();
protected abstract void loadData();
@Override
protected void onPause() {
super.onPause();
removeActivity(this);
}
private void addActivity(Activity activity) {
if (activity != null && !activities.contains(activity)) {
activities.add(activity);
}
}
private void removeActivity(Activity activity) {
if (activity != null && activities.contains(activity)) {
activities.remove(activity);
}
}
public static void exit() {
if (activities != null && activities.size() > 0) {
for (Activity activity : activities) {
activity.finish();
}
}
System.exit(0);
}
}
2.ToolbarActivity is a title bar encapsulation for every activity; it is mainly to save code and make the activity inherited from this activity more logical.
public abstract class ToolbarActivity extends BaseActivity {
@BindView(R.id.toolbar)
Toolbar toolbar;
@BindView(R.id.title)
public TextView title;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
toolbar.setTitle("");
setSupportActionBar(toolbar);
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
}
}
3.DrawerActivity is a drawer activity; in this way, letting MainActivity inherit makes main's code more concise and reusable.
public abstract class DrawerActivity extends ToolbarActivity implements NavigationView.OnNavigationItemSelectedListener {
@BindView(R.id.navigation_view)
NavigationView navigationView;
@BindView(R.id.drawer_layout)
DrawerLayout drawerLayout;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActionBarDrawerToggle drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, toolbar, R.string.drawer_open, R.string.drawer_close);
drawerToggle.syncState();
drawerLayout.setDrawerListener(drawerToggle);
drawerToggle.setDrawerIndicatorEnabled(false);
toolbar.setNavigationIcon(R.drawable.toolbar_navigation);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
drawerLayout.openDrawer(Gravity.LEFT);
}
});
navigationView.setNavigationItemSelectedListener(this);
}
@Override
public boolean onNavigationItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.publish:
drawerLayout.closeDrawers();
return true;
case R.id.published:
drawerLayout.closeDrawers();
return true;
case R.id.growth:
drawerLayout.closeDrawers();
break;
case R.id.collect:
drawerLayout.closeDrawers();
return true;
case R.id.setting:
drawerLayout.closeDrawers();
return true;
}
return super.onContextItemSelected(item);
}
}
So the code to inherit DrawerActivity's MainActivity is very simple, and other activities can also inherit ToolbarActivity to achieve reuse.
public class MainActivity extends DrawerActivity {
private static final int PAGE_LIMIT = 4;
@BindView(R.id.tab_layout)
TabLayout tabLayout;
@BindView(R.id.viewpager)
NoScrollViewPager viewPager;
@Override
protected int getLayoutId() {
return R.layout.activity_main;
}
@Override
protected void initView() {
}
@Override
protected void initEvent() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
String packageName = getPackageName();
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
if (!pm.isIgnoringBatteryOptimizations(packageName)) {
Intent intent = new Intent();
intent.setAction(android.provider.Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
intent.setData(Uri.parse("package:" + packageName));
startActivity(intent);
}
}
}
@Override
protected void loadData() {
List<Fragment> fragments = new ArrayList<>();
fragments.add(new AbilityFragment());
fragments.add(new AttentionFragment());
fragments.add(new DiscoveryFragment());
fragments.add(new ArenaFragment());
ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager(), fragments);
viewPager.setAdapter(adapter);
viewPager.setOffscreenPageLimit(PAGE_LIMIT);
tabLayout.setupWithViewPager(viewPager);
for (int i = 0; i < tabLayout.getTabCount(); i++) {
TabLayout.Tab tab = tabLayout.getTabAt(i);
Drawable drawable = null;
switch (i) {
case 0:
drawable = ContextCompat.getDrawable(this, R.drawable.tab_item_home);
break;
case 1:
drawable = ContextCompat.getDrawable(this, R.drawable.tab_item_attention);
break;
case 2:
drawable = ContextCompat.getDrawable(this, R.drawable.tab_item_discovery);
break;
case 3:
drawable = ContextCompat.getDrawable(this, R.drawable.tab_item_arena);
break;
}
if (tab != null) {
tab.setIcon(drawable);
}
}
}
}
Next, let's look at fragment encapsulation:
Likewise, we can see that I have written three abstract fragment s for different businesses.
1. Base fragment. The main method is template mode, lazy loading of fragments.
Parsing lazy loading: The setUserVisibleHint method is used to tell the system whether the UI of the Fragment is visible. So we only need to inherit fragments and rewrite this method to implement data loading operation when fragments are visible, that is, lazy loading of fragments.
public abstract class BaseFragment extends Fragment {
private Handler handler = new Handler();
private Runnable loadDataTask = new Runnable() {
@Override
public void run() {
loadData();
}
};
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(getLayoutId(), container, false);
ButterKnife.bind(this, view);
return view;
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
initView();
initEvent();
handler.postDelayed(loadDataTask, 500);
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (!isVisibleToUser) {
handler.removeCallbacks(loadDataTask);
}
}
protected abstract void initView();
protected abstract void initEvent();
protected abstract void loadData();
protected abstract int getLayoutId();
}
2. RecyclerView Fragment, because RecyclerView accounts for a lot of parts in multi-list app s, so I take it as a layer of fragment to write, to facilitate code reuse. In addition, this fragment also completes the drop-down refresh message notification and reuse of subclasses.
public abstract class RecyclerViewFragment extends BaseFragment implements SwipeRefreshLayout.OnRefreshListener {
protected static final int REFRESH = 0x101;
@BindView(R.id.recycler_view)
RecyclerView recyclerView;
@BindView(R.id.swipeRefreshLayout)
SwipeRefreshLayout swipeRefreshLayout;
protected RecyclerView.Adapter adapter;
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case REFRESH:
swipeRefreshLayout.setRefreshing(false);
break;
}
}
};
@Override
protected void initView() {
FullyLinearLayoutManager layoutManager = new FullyLinearLayoutManager(getActivity(), LinearLayout.VERTICAL, false);
layoutManager.setSmoothScrollbarEnabled(true);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setNestedScrollingEnabled(false);
swipeRefreshLayout.setSize(SwipeRefreshLayout.LARGE);
swipeRefreshLayout.setColorSchemeResources(R.color.colorArenaGreen, R.color.colorAccent, R.color.colorMainDividerLine);
}
@Override
protected void initEvent() {
swipeRefreshLayout.setOnRefreshListener(this);
}
@Override
protected void loadData(){
adapterBuilder();
recyclerView.setAdapter(adapter);
}
protected abstract void adapterBuilder();
@Override
public void onRefresh() {
handler.sendEmptyMessageDelayed(REFRESH, 3000);
}
}
3. Banner Fragment mainly completes the encapsulation of the round-robin map. Considering the advertisement, many app s provide a layer.
public abstract class BannerFragment extends RecyclerViewFragment implements OnItemClickListener {
private static final long LOOP_TIME = 5000;
@BindView(R.id.banner)
ConvenientBanner<String> convenientBanner;
protected abstract List<String> getBitmapList();
protected void loadConvenientBanner() {
convenientBanner.setPages(new CBViewHolderCreator<LocalImageHolderView>() {
@Override
public LocalImageHolderView createHolder() {
return new LocalImageHolderView();
}
}, getBitmapList())
.setPageIndicator(new int[]{R.drawable.page_indicator, R.drawable.page_indicator_focused})
.setOnItemClickListener(this);
convenientBanner.startTurning(LOOP_TIME);
}
@Override
protected void initView() {
super.initView();
convenientBanner.setPages(new CBViewHolderCreator<LocalImageHolderView>() {
@Override
public LocalImageHolderView createHolder() {
return new LocalImageHolderView();
}
}, getBitmapList())
.setPageIndicator(new int[]{R.drawable.page_indicator, R.drawable.page_indicator_focused})
.setOnItemClickListener(this);
convenientBanner.startTurning(LOOP_TIME);
}
@Override
protected void loadData(){
super.loadData();
}
public class LocalImageHolderView implements Holder<String> {
private ImageView imageView;
@Override
public View createView(Context context) {
imageView = new ImageView(context);
imageView.setScaleType(ImageView.ScaleType.FIT_XY);
return imageView;
}
@Override
public void UpdateUI(Context context, final int position, String data) {
Glide.with(context).load(data).into(imageView);
}
}
}
Okay, one of the Android-multi-list projects (Rxjava+Rtrofit+Recyclerview+Glide+Adapter encapsulation) is finished. This blog is the first in this series, so it's just like the framework of the project. In addition, this series will be updated step by step. I will finish it as soon as possible and share my experience with you. Welcome to point out the mistakes below and learn together!! Your praise is my best support!!
Reprinted please note: [Jack Frost's blog]