Android - Project Architecture of Multiple Lists (Rxjava+Rtrofit+Recyclerview+Glide+Adapter Encapsulation)

Keywords: Fragment DrawerLayout Android network

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 {
   //Store all activities
    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);//ButterKnife has to bing here, which means initialization, so the rest of the activity doesn't have to take this into account.

        initView();//Template mode
        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);//Release all resources
    }


    // Add Activity to the container
    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);
        }
    }

    // Exit the entire APP
    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("");//Save code, and app activity won't have to worry about the title in the future
        setSupportActionBar(toolbar);
        if (getSupportActionBar() != null) {
            getSupportActionBar().setDisplayHomeAsUpEnabled(true);//Determine whether the icon in the upper left corner can be clicked
        }
        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();//This method automatically associates with actionBar and displays the picture of the switch on the action. If not set, it can also have the effect of drawer, but it is the default icon.  
        drawerLayout.setDrawerListener(drawerToggle);

        drawerToggle.setDrawerIndicatorEnabled(false);
        toolbar.setNavigationIcon(R.drawable.toolbar_navigation);//Setting up the drawer picture, mainly depends on the artist, the artist should specify the size of the picture, I put it here casually.
        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:
            //The jump activity is built here
//                startActivity(new Intent(this, PublishActivity.class));
                drawerLayout.closeDrawers();
                return true;


            case R.id.published:
//                startActivity(new Intent(this, PublishedActivity.class));
                drawerLayout.closeDrawers();
                return true;


            case R.id.growth:
//                startActivity(new Intent(this, GrowthActivity.class));
                drawerLayout.closeDrawers();
                break;


            case R.id.collect:
//                startActivity(new Intent(this, CollectionActivity.class));
                drawerLayout.closeDrawers();
                return true;

            case R.id.setting:
//                startActivity(new Intent(this, SettingActivity.class));
                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;//This is to limit the number of pre-loaded pages, without fragmentation to go back to onCreate. Using this is to optimize the page, so that the viewpager caches the view
    @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();
        // Delay loading data to reduce carton
        handler.postDelayed(loadDataTask, 500);
    }
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        //A judgment is made that isVisibleToUser only loadData() method is executed to load network (or local) data if it is true.
        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;
//A handle lets the subclass override onrefresh to notify the parent class, which then calls handler for reuse.
    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);
    }
    //Sub activity Setup Adapter
    protected abstract void adapterBuilder();
    @Override
    public void onRefresh() {
        //todo network request task
        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;

//This round-robin uses third-party controls and a library for convenience in actual project development.
    @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);
        }
    }
}

Source download: Android - Multiple List Items (Rxjava+Rtrofit+Recyclerview+Glide+Adapter Encapsulation)

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]

More content, accessible Jack Frost's blog

Posted by PakiGangsta on Sat, 22 Dec 2018 06:06:05 -0800