• TouTiao开源项目 分析笔记4==>一个简单APP 整体常用框架


    1.效果预览

    1.1.如下图所以,到目前为止所有的功能。

      


    2.从InitApp开始->SplashActivity->MainActivity

    2.1.InitApp源代码。这是整个项目的Application。

    public class InitApp extends MultiDexApplication {
        public static Context AppContext;
    
        @Override
        public void onCreate(){
            super.onCreate();
    
            AppContext=getApplicationContext();
    
            //初始化主题
            initTheme();
            //错误日志分析
            CrashHandlerUtil.getInstance().init(AppContext);
    
            if(BuildConfig.DEBUG){
                SDKManager.initStetho(AppContext);
            }
    
        }
        private void initTheme(){
    
        }
    }

    2.2.启动页面SplashActivity

    class SplashActivity : AppCompatActivity() {
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            val intent= Intent(this, MainActivity::class.java)
            startActivity(intent)
            finish()
        }
    }

    2.3.主页面MainActivity

    class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedListener {
    
        companion object {
            private val TAG="MainActivity"
            private val POSITION="position"
            private val SELECT_ITEM="bottomNavigationSelectItem"
            private val FRAGMENT_NEWS=0
            private val FRAGMENT_PHOTO=1
            private val FRAGMENT_VIDEO=2
            private val FRAGMENT_MEDIA=3
            private val REQUEST_EXTERNAL_STORAGE = 1
            private val PERMISSIONS_STORAGE = arrayOf("android.permission.READ_EXTERNAL_STORAGE", "android.permission.WRITE_EXTERNAL_STORAGE")
    
        }
        private var newsTabLayout: NewsTabLayout?=null   //新闻布局
        private var photoTabLayout: PhotoTabLayout?=null     //图片布局
        private var videoTabLayout: VideoTabLayout?=null     //视频布局
        private var mediaChannelView: MediaChannelView?=null    //头条号布局
        private var toolbar: Toolbar?=null    //标题栏
        private var bottom_navigation:BottomNavigationView?=null
        private var exitTime:Long=0
        private var position:Int=0
        private var firstClickTime:Long=0
        private var nav_view:NavigationView?=null
        private var drawer_layout:DrawerLayout?=null
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            initView()
            verifyStoragePermissions(this)
            if(savedInstanceState!=null){
                newsTabLayout=supportFragmentManager!!.findFragmentByTag(NewsTabLayout::class.java.name) as NewsTabLayout
                photoTabLayout=supportFragmentManager!!.findFragmentByTag(PhotoTabLayout::class.java.name) as PhotoTabLayout
                videoTabLayout=supportFragmentManager!!.findFragmentByTag(VideoTabLayout::class.java.name) as VideoTabLayout
                mediaChannelView=supportFragmentManager!!.findFragmentByTag(MediaChannelView::class.java.name) as MediaChannelView
                //恢复 recreate 前的位置
                showFragment(savedInstanceState.getInt(POSITION))
                bottom_navigation!!.selectedItemId=savedInstanceState.getInt(SELECT_ITEM)
            }else{
                showFragment(FRAGMENT_NEWS)
            }
    
            if(SettingUtil.getInstance().getIsFirstTime()){
                showTapTarget()
            }
        }
    
        private fun showTapTarget(){
            val display=windowManager.defaultDisplay
            val target=Rect(0,display.height,0,display.height)
            target.offset(display.width/8,-56)
    
            val sequence=TapTargetSequence(this)
                    .targets(
                         TapTarget.forToolbarMenuItem(toolbar!!,R.id.action_search,"点击这里进行搜索")
                                 .dimColor(android.R.color.black)
                                 .outerCircleColor(R.color.colorPrimary)
                                 .drawShadow(true)
                                 .id(1),
                         TapTarget.forToolbarNavigationIcon(toolbar!!,"点击这里展开侧栏")
                                 .dimColor(android.R.color.black)
                                 .outerCircleColor(R.color.colorPrimary)
                                 .drawShadow(true)
                                 .id(2),
                         TapTarget.forBounds(target,"点击这里切换新闻
    再次点击刷新当前页面")
                                 .dimColor(android.R.color.black)
                                 .outerCircleColor(R.color.colorPrimary)
                                 .targetRadius(60)
                                 .transparentTarget(true)
                                 .drawShadow(true)
                                 .id(3)
                    ).listener(object:TapTargetSequence.Listener{
                override fun onSequenceFinish() {
                    SettingUtil.getInstance().isFirstTime=false
                }
    
                override fun onSequenceStep(lastTarget: TapTarget?, targetClicked: Boolean) {
    
                }
    
                override fun onSequenceCanceled(lastTarget: TapTarget?) {
    
                }
            })
            sequence.start()
        }
    
        override fun initSlidable() {
            //禁止滑动返回
            //什么都不写就行了
        }
    
        override fun onSaveInstanceState(outState: Bundle?) {
            outState!!.putInt(POSITION,position)
            outState!!.putInt(SELECT_ITEM,bottom_navigation!!.selectedItemId)
        }
    
        private fun initView(){
            toolbar=findViewById(R.id.toolbar) as Toolbar
            toolbar!!.inflateMenu(R.menu.menu_activity_main)
            bottom_navigation=findViewById(R.id.bottom_navigation) as BottomNavigationView
            //解决只有图标没有文字的问题
            BottomNavigationViewHelper.disableShiftMode(bottom_navigation)
            setSupportActionBar(toolbar)
            setTitle(R.string.title_news)
            bottom_navigation!!.setOnNavigationItemSelectedListener{ item ->
                when(item.itemId){
                    R.id.action_news -> {
                        showFragment(FRAGMENT_NEWS)
                        doubleClick(FRAGMENT_NEWS)
                    }
                    R.id.action_photo -> {
                        showFragment(FRAGMENT_PHOTO)
                        doubleClick(FRAGMENT_PHOTO)
                    }
                    R.id.action_video -> {
                        showFragment(FRAGMENT_VIDEO)
                        doubleClick(FRAGMENT_VIDEO)
                    }
                    R.id.action_media -> {
                        showFragment(FRAGMENT_MEDIA)
                    }
                }
                true
            }
    
            drawer_layout=findViewById(R.id.drawer_layout) as DrawerLayout
            val toggle=ActionBarDrawerToggle(this,drawer_layout,toolbar,R.string.navigation_drawer_open,R.string.navigation_drawer_close)
            drawer_layout!!.addDrawerListener(toggle)
            toggle.syncState()
    
            nav_view=findViewById(R.id.nav_view) as NavigationView
            nav_view!!.setNavigationItemSelectedListener(this)
        }
    
        fun doubleClick(index:Int){
            val secondClickTime=System.currentTimeMillis()
            if((secondClickTime-firstClickTime)<500){
                when(index){
                    FRAGMENT_NEWS -> {
    
                    }
                    FRAGMENT_PHOTO ->{
    
                    }
                    FRAGMENT_VIDEO -> {
    
                    }
                }
            }else{
                firstClickTime=secondClickTime
            }
        }
    
        private fun showFragment(index:Int){
           val ft=supportFragmentManager.beginTransaction()
            hideFragment(ft)
            position=index
            when(index){
               FRAGMENT_NEWS -> {
                  toolbar!!.setTitle(R.string.title_news)
                  if(newsTabLayout==null){
                      newsTabLayout = NewsTabLayout.getInstance()
                      ft.add(R.id.container, newsTabLayout, NewsTabLayout::class.java.name)
                  }else{
                      ft.show(newsTabLayout)
                  }
               }
               FRAGMENT_PHOTO -> {
                   toolbar!!.setTitle(R.string.title_photo)
                   if(photoTabLayout==null){
                       photoTabLayout = PhotoTabLayout.getInstance()
                       ft.add(R.id.container, photoTabLayout, PhotoTabLayout::class.java.name)
                   }else{
                       ft.show(photoTabLayout)
                   }
               }
               FRAGMENT_VIDEO -> {
                   toolbar!!.setTitle(R.string.title_video)
                   if(videoTabLayout==null){
                       videoTabLayout = VideoTabLayout.getInstance()
                       ft.add(R.id.container, videoTabLayout, VideoTabLayout::class.java.name)
                   }else{
                       ft.show(videoTabLayout)
                   }
               }
               FRAGMENT_MEDIA -> {
                   toolbar!!.setTitle(R.string.title_media)
                   if(mediaChannelView==null){
    
                   }else{
    
                   }
               }
            }
            ft.commit()
        }
    
        private fun hideFragment(ft:FragmentTransaction){
            //如果不为空,就先隐藏起来
            if(newsTabLayout!=null){
                ft.hide(newsTabLayout)
            }
            if(photoTabLayout!=null){
                ft.hide(photoTabLayout)
            }
            if(videoTabLayout!=null){
                ft.hide(videoTabLayout)
            }
            if(mediaChannelView!=null){
                ft.hide(mediaChannelView as Fragment)
            }
        }
    
        override fun onBackPressed() {
            //再次点击退出
            val currentTime=System.currentTimeMillis()
            if((currentTime-exitTime)<2000){
                super.onBackPressed()
            }else{
                Toast.makeText(this,R.string.double_click_exit,Toast.LENGTH_SHORT).show()
                exitTime=currentTime
            }
        }
    
        override fun onCreateOptionsMenu(menu: Menu): Boolean {
            menuInflater.inflate(R.menu.menu_activity_main, menu)
            return true
        }
    
        override fun onOptionsItemSelected(item: MenuItem): Boolean {
            if (item.itemId == R.id.action_search) {
               //跳转到搜索页面
            }
            return super.onOptionsItemSelected(item)
        }
    
        override fun onNavigationItemSelected(item: MenuItem): Boolean {
            //
            val id=item.itemId
            when(id){
                R.id.nav_switch_night_mode ->{
    
                }
                R.id.nav_setting ->{
    
                }
                R.id.nav_share -> {
    
                }
            }
            return false
        }
    
    
        fun verifyStoragePermissions(activity: Activity) {
                try {
                    //检测是否有写的权限
                    val permission = ActivityCompat.checkSelfPermission(activity,
                            "android.permission.WRITE_EXTERNAL_STORAGE")
                    if (permission != PackageManager.PERMISSION_GRANTED) {
                        // 没有写的权限,去申请写的权限,会弹出对话框
                        ActivityCompat.requestPermissions(activity, PERMISSIONS_STORAGE, REQUEST_EXTERNAL_STORAGE)
                    }
                } catch (e: Exception) {
                    e.printStackTrace()
                }
        }
    
    }
    View Code

    2.4.MainActivity布局

    <?xml version="1.0" encoding="utf-8"?>
    <android.support.v4.widget.DrawerLayout 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:id="@+id/drawer_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true"
        tools:openDrawer="start"
        >
    
       <android.support.design.widget.CoordinatorLayout
           android:layout_width="match_parent"
           android:layout_height="match_parent"
           >
    
          <include layout="@layout/toolbar"/>
    
          <include layout="@layout/container"/>
    
          <android.support.design.widget.BottomNavigationView
              android:id="@+id/bottom_navigation"
              style="@style/Widget.Design.BottomNavigationView"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:layout_alignParentBottom="true"
              android:layout_gravity="bottom"
              android:background="@color/viewBackground"
              app:elevation="16dp"
              app:itemIconTint="@drawable/nav_item_color_state"
              app:itemTextColor="@drawable/nav_item_color_state"
              app:menu="@menu/bottom_navigation_main"
              />
    
       </android.support.design.widget.CoordinatorLayout>
    
       <android.support.design.widget.NavigationView
           android:id="@+id/nav_view"
           android:layout_width="wrap_content"
           android:layout_height="match_parent"
           android:layout_gravity="start"
           android:fitsSystemWindows="true"
           app:headerLayout="@layout/nav_header_main"
           app:menu="@menu/nav_menu"
           />
    
    </android.support.v4.widget.DrawerLayout>
    View Code

         

      @layout/toolbar布局 

    <?xml version="1.0" encoding="utf-8"?>
    <android.support.design.widget.AppBarLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/AppBarLayout01"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:fitsSystemWindows="false"
        android:theme="@style/AppTheme.AppBarOverlay"
        app:elevation="0dp"
        >
    
        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            app:layout_scrollFlags="scroll|enterAlways"
            app:popupTheme="@style/AppTheme.PopupOverlay"
            />
    
    
    </android.support.design.widget.AppBarLayout>
    View Code

      

      @layout/container布局

    <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout 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:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/viewBackground"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        tools:layout="@layout/fragment_news_tab"
        >
    
    </FrameLayout>
    View Code

    2.5.BaseActivity为基类

    public class BaseActivity extends RxAppCompatActivity {
        private static final String TAG="BaseActivity";
        protected SlidrInterface slidrInterface;
        private int iconType=-1;
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState){
            super.onCreate(savedInstanceState);
            //默认图标获取的是圆形circle
            this.iconType=SettingUtil.getInstance().getCustomIconValue();
            initSlidable();
        }
    
        @Override
        protected void onResume(){
            super.onResume();
            //获取主题色
            int color=SettingUtil.getInstance().getColor();
            //获取图标形状,圆,矩,正
            int drawable=Constant.ICONS_DRAWABLES[SettingUtil.getInstance().getCustomIconValue()];
            if(getSupportActionBar()!=null){
                //设置标题栏的颜色
                getSupportActionBar().setBackgroundDrawable(new ColorDrawable(color));
            }
            //如果SDK版本>=21,还要设置状态栏的颜色
            if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.LOLLIPOP){
                getWindow().setStatusBarColor(CircleView.shiftColorDown(color));
                //TaskDescription==>用于在最近的任务列表中设置和检索当前活动的信息
                ActivityManager.TaskDescription taskDescription=new ActivityManager.TaskDescription(
                        getString(R.string.app_name),
                        BitmapFactory.decodeResource(getResources(),drawable),
                        color
                );
                setTaskDescription(taskDescription);
                //setNavigationBar是底部导航栏,模拟器上会有
                if(SettingUtil.getInstance().getNavBar()){
                    getWindow().setNavigationBarColor(CircleView.shiftColorDown(color));
                }else{
                    getWindow().setNavigationBarColor(Color.BLACK);
                }
            }
        }
    
        @Override
        public boolean onOptionsItemSelected(MenuItem item){
            if(item.getItemId()==android.R.id.home){
                onBackPressed();
            }
            return super.onOptionsItemSelected(item);
        }
    
        @Override
        public void onBackPressed(){
            //Fragment逐个出栈
            int count=getSupportFragmentManager().getBackStackEntryCount();
            if(count==0){
                super.onBackPressed();
            }else{
                getSupportFragmentManager().popBackStack();
            }
        }
    
        @Override
        protected void onStop(){
    
            //如果iconType不为默认的圆形了
            if(iconType!=SettingUtil.getInstance().getCustomIconValue()){
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        //前提是在清单中已经完成配置别名
                        String act=".SplashActivity_";
                        for(String s:Constant.ICONS_TYPE){
                            getPackageManager().setComponentEnabledSetting(new ComponentName(BaseActivity.this,getPackageName()+act+s),
                                    PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
                                    PackageManager.DONT_KILL_APP);
                        }
    
                        act+=Constant.ICONS_TYPE[SettingUtil.getInstance().getCustomIconValue()];
    
                        getPackageManager().setComponentEnabledSetting(new ComponentName(BaseActivity.this,getPackageName()+act),
                                PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
                                PackageManager.DONT_KILL_APP);
                    }
                }).start();
            }
    
            super.onStop();
        }
    
        /**
         * 初始化Toolbar
         */
        protected void initToolbar(Toolbar toolbar, boolean homeAsUpEnabled, String title){
            toolbar.setTitle(title);
            setSupportActionBar(toolbar);
            getSupportActionBar().setDisplayHomeAsUpEnabled(homeAsUpEnabled);
        }
    
        /**
         * 初始化滑动返回
         */
        protected void initSlidable(){
            int isSlidable=SettingUtil.getInstance().getSlidable();
            if(isSlidable!= Constant.SLIDABLE_DISABLE){
                SlidrConfig config=new SlidrConfig.Builder()
                                        .edge(isSlidable==Constant.SLIDABLE_EDGE)
                                        .build();
                slidrInterface= Slidr.attach(this,config);
            }
        }
    
    }
    View Code

    2.6.常量定义类

    public class Constant {
        public static final String USER_AGENT_MOBILE="Mozilla/5.0 (Linux; Android 5.1.1; Nexus 6 Build/LYZ28E) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Mobile Safari/537.36";
    
        public static final String USER_AGENT_PC="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36";
    
        public static final int[] TAG_COLORS = new int[]{
                Color.parseColor("#90C5F0"),
                Color.parseColor("#91CED5"),
                Color.parseColor("#F88F55"),
                Color.parseColor("#C0AFD0"),
                Color.parseColor("#E78F8F"),
                Color.parseColor("#67CCB7"),
                Color.parseColor("#F6BC7E")
        };
    
        public static final int[] ICONS_DRAWABLES = new int[]{
                R.mipmap.ic_launcher_circle,
                R.mipmap.ic_launcher_rect,
                R.mipmap.ic_launcher_square
        };
    
        public static final String[] ICONS_TYPE=new String[]{"circle","rect","square"};
    
        public static final int SLIDABLE_DISABLE=0;
        public static final int SLIDABLE_EDGE=1;
        public static final int SLIDABLE_FULL=2;
    
        public static final String AS="as";
        public static final String CP="cp";
    
        public static final int NEWS_CHANNEL_ENABLE=1;
        public static final int NEWS_CHANNEL_DISABLE=0;
    }
    View Code

    2.7.方便开发者测试类SDKManager

    package com.jasonjan.headnews.global;
    
    import android.content.Context;
    
    import com.facebook.stetho.Stetho;
    
    import okhttp3.OkHttpClient;
    import okhttp3.logging.HttpLoggingInterceptor;
    
    /**
     * Created by JasonJan on 2017/11/30.
     */
    
    public class SDKManager {
        public static void initStetho(Context context){
            Stetho.initializeWithDefaults(context);
        }
    
        public static OkHttpClient.Builder initInterceptor(OkHttpClient.Builder builder){
            HttpLoggingInterceptor interceptor=new HttpLoggingInterceptor();
            interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
            builder.addInterceptor(interceptor);
            return builder;
        }
    }
    View Code

    2.8.异常处理类CrashHandleUtil

    package com.jasonjan.headnews.util;
    
    import android.content.Context;
    import android.content.pm.PackageInfo;
    import android.content.pm.PackageManager;
    import android.os.Build;
    import android.os.Environment;
    import android.os.Process;
    import android.util.Log;
    
    import java.io.BufferedWriter;
    import java.io.File;
    import java.io.FileWriter;
    import java.io.IOException;
    import java.io.PrintWriter;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    /**
     * Created by JasonJan on 2017/12/2.
     */
    
    public class CrashHandlerUtil implements Thread.UncaughtExceptionHandler {
        private static final String TAG = "异常处理";
        private static final boolean DEBUG = true;
    
        private static final String PATH = Environment.getExternalStorageDirectory().getPath() +
                "/toutiao/log/";
        private static final String FILE_NAME = "crash";
    
        //log文件的后缀名
        private static final String FILE_NAME_SUFFIX = ".txt";
    
        private static CrashHandlerUtil sInstance = new CrashHandlerUtil();
    
        //系统默认的异常处理(默认情况下,系统会终止当前的异常程序)
        private Thread.UncaughtExceptionHandler mDefaultCrashHandler;
    
        private Context mContext;
    
        //构造方法私有,防止外部构造多个实例,即采用单例模式
        private CrashHandlerUtil() {
        }
    
        public static CrashHandlerUtil getInstance() {
            return sInstance;
        }
    
        //这里主要完成初始化工作
        public void init(Context context) {
            //获取系统默认的异常处理器
            mDefaultCrashHandler = Thread.getDefaultUncaughtExceptionHandler();
            //将当前实例设为系统默认的异常处理器
            Thread.setDefaultUncaughtExceptionHandler(this);
            //获取Context,方便内部使用
            mContext = context.getApplicationContext();
    
        }
    
        /**
         * 这个是最关键的函数,当程序中有未被捕获的异常,系统将会自动调用#uncaughtException方法
         * thread为出现未捕获异常的线程,ex为未捕获的异常,有了这个ex,我们就可以得到异常信息。
         */
        @Override
        public void uncaughtException(Thread thread, Throwable ex) {
            try {
                //导出异常信息到SD卡中
                dumpExceptionToSDCard(ex);
                //这里可以通过网络上传异常信息到服务器,便于开发人员分析日志从而解决bug
                uploadExceptionToServer();
            } catch (IOException e) {
                e.printStackTrace();
            }
    
            //打印出当前调用栈信息
            ex.printStackTrace();
    
            //如果系统提供了默认的异常处理器,则交给系统去结束我们的程序,否则就由我们自己结束自己
            if (mDefaultCrashHandler != null) {
                mDefaultCrashHandler.uncaughtException(thread, ex);
            } else {
                Process.killProcess(Process.myPid());
            }
    
        }
    
        private void dumpExceptionToSDCard(Throwable ex) throws IOException {
            //如果SD卡不存在或无法使用,则无法把异常信息写入SD卡
            if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
                if (DEBUG) {
                    Log.w(TAG, "sdcard unmounted,skip dump exception");
                    return;
                }
            }
    
            File dir = new File(PATH);
            if (!dir.exists()) {
                dir.mkdirs();
            }
            long current = System.currentTimeMillis();
            String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(current));
            //以当前时间创建log文件
            File file = new File(PATH + FILE_NAME + time + FILE_NAME_SUFFIX);
            if (!file.exists()) {
                Log.i(TAG, "文件名称为:"+file.getName());
            }else{
                Log.i(TAG, "文件名称为:"+file.getName());
            }
    
            try {
                if(file.createNewFile()){
                    Log.i(TAG, "文件创建成功:名称为:"+file.getName());
                }
                if(file.exists()){
                    Log.i(TAG, "文件exists");
                }
                PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(file)));
                //导出发生异常的时间
                pw.println(time);
    
                //导出手机信息
                dumpPhoneInfo(pw);
    
                pw.println();
                //导出异常的调用栈信息
                ex.printStackTrace(pw);
    
                pw.close();
            } catch (Exception e) {
                Log.e(TAG, "dump crash info failed");
            }
        }
    
        private void dumpPhoneInfo(PrintWriter pw) throws PackageManager.NameNotFoundException {
            //应用的版本名称和版本号
            PackageManager pm = mContext.getPackageManager();
            PackageInfo pi = pm.getPackageInfo(mContext.getPackageName(), PackageManager
                    .GET_ACTIVITIES);
            pw.print("App Version: ");
            pw.print(pi.versionName);
            pw.print('_');
            pw.println(pi.versionCode);
    
            //android版本号
            pw.print("OS Version: ");
            pw.print(Build.VERSION.RELEASE);
            pw.print("_");
            pw.println(Build.VERSION.SDK_INT);
    
            //手机制造商
            pw.print("Vendor: ");
            pw.println(Build.MANUFACTURER);
    
            //手机型号
            pw.print("Model: ");
            pw.println(Build.MODEL);
    
            //cpu架构
            pw.print("CPU ABI: ");
            pw.println(Build.CPU_ABI);
        }
    
        private void uploadExceptionToServer() {
            //TODO Upload Exception Message To Your Web Server
        }
    
    }
    View Code

    2.9.网络工具类NetWorkUtil

    package com.jasonjan.headnews.util;
    
    import android.content.Context;
    import android.net.ConnectivityManager;
    import android.net.NetworkInfo;
    
    /**
     * Created by JasonJan on 2017/11/30.
     */
    
    public class NetWorkUtil {
    
        public static boolean isNetWorkConnected(Context context){
            if(context!=null){
                ConnectivityManager manager=(ConnectivityManager)
                        context.getSystemService(Context.CONNECTIVITY_SERVICE);
                NetworkInfo networkInfo=manager.getActiveNetworkInfo();
                return null != networkInfo&&networkInfo.isAvailable();
            }
            return false;
        }
    
        public static boolean isWifiConnected(Context context){
            if(context!=null){
                ConnectivityManager manager=(ConnectivityManager)
                        context.getSystemService(Context.CONNECTIVITY_SERVICE);
                NetworkInfo netWorkInfo=manager.getActiveNetworkInfo();
                if(null!=netWorkInfo&&netWorkInfo.getType()==ConnectivityManager.TYPE_WIFI){
                    return netWorkInfo.isAvailable();
                }
            }
            return false;
        }
    
        public static boolean isMobileConnected(Context context){
            if(context!=null){
                ConnectivityManager manager=(ConnectivityManager)
                        context.getSystemService(Context.CONNECTIVITY_SERVICE);
                NetworkInfo networkInfo=manager.getActiveNetworkInfo();
                if(null!=networkInfo&&networkInfo.getType()==ConnectivityManager.TYPE_MOBILE){
                    return networkInfo.isAvailable();
                }
            }
            return false;
        }
    
    }
    View Code

    2.10.项目设置通用类

    package com.jasonjan.headnews.util;
    
    import android.content.SharedPreferences;
    import android.graphics.Color;
    import android.preference.PreferenceManager;
    
    import com.jasonjan.headnews.R;
    import com.jasonjan.headnews.global.InitApp;
    
    /**
     * Created by JasonJan on 2017/11/30.
     */
    
    public class SettingUtil {
        private SharedPreferences setting= PreferenceManager.getDefaultSharedPreferences(InitApp.AppContext);
    
        private static final class SettingUtilInstance{
            private static final SettingUtil instance=new SettingUtil();
        }
    
        public static SettingUtil getInstance(){
            return SettingUtilInstance.instance;
        }
    
        /**
         * 获取是否开启无图模式
         * @return
         */
        public boolean getIsNoPhotoMode(){
            return setting.getBoolean("switch_noPhotoMode",false)&& NetWorkUtil.isMobileConnected(InitApp.AppContext);
        }
    
        /**
         * 获取主题颜色
         * @return
         */
        public int getColor(){
            int defaultColor=InitApp.AppContext.getResources().getColor(R.color.colorPrimary);
            int color=setting.getInt("color",defaultColor);
            if((color!=0)&& Color.alpha(color)!=255){
                return defaultColor;
            }
            return color;
        }
    
        /**
         * 设置主题颜色
         * @param color
         */
        public void setColor(int color){
            setting.edit().putInt("color",color).apply();
        }
    
        /**
         * 获取是否开启夜间模式
         * @return
         */
        public boolean getIsNightMode(){
            return setting.getBoolean("switch_nightMode",false);
        }
    
        /**
         * 设置夜间模式
         * @param flag
         */
        public void setIsNightMode(boolean flag){
            setting.edit().putBoolean("switch_nightMode",flag).apply();
        }
    
        /**
         * 获取是否开启自动切换夜间模式
         * @return
         */
        public boolean getIsAutoNightMode(){
            return setting.getBoolean("auto_nightMode",false);
        }
    
        /**
         * 设置开启自动切换夜间模式
         * @param flag
         */
        public void setIsAutoNightMode(boolean flag){
            setting.edit().putBoolean("auto_nightMode",flag).apply();
        }
    
        public String getNightStartHour(){
            return setting.getString("night_startHour","22");
        }
    
        public void setNightStartHour(String nightStartHour){
            setting.edit().putString("night_startHour",nightStartHour).apply();
        }
    
        public String getNightStartMinute(){
            return setting.getString("night_startMinute","00");
        }
    
        public void setNightStartMinute(String nightStartMinute){
            setting.edit().putString("night_startMinute",nightStartMinute).apply();
        }
    
        public String getDayStartHour(){
            return setting.getString("day_startHour","06");
        }
    
        public void setDayStartHour(String day_startHour){
            setting.edit().putString("day_startHour",day_startHour).apply();
        }
    
        public String getDayStartMinute(){
            return setting.getString("day_startMinute","00");
        }
    
        public void setDayStartMinute(String day_startMinute){
            setting.edit().putString("day_startMinute",day_startMinute).apply();
        }
    
        /**
         * 获取是否开启导航栏上色
         * @return
         */
        public boolean getNavBar(){
            return setting.getBoolean("nav_bar",false);
        }
    
        /**
         * 获取是否开启视频强制横屏
         * @return
         */
        public boolean getIsVideoForceLandscape(){
            return setting.getBoolean("video_force_landscape",false);
        }
    
        /**
         * 获取图标值
         * @return
         */
        public int getCustomIconValue(){
            String s=setting.getString("custom_icon","0");
            return Integer.parseInt(s);
        }
    
        /**
         * 获取滑动返回值
         * @return
         */
        public int getSlidable(){
            String s=setting.getString("slidable","1");
            return Integer.parseInt(s);
        }
    
        /**
         * 获取是否开启视频自动播放
         * @return
         */
        public boolean getIsVideoAutoPlay(){
            return setting.getBoolean("video_auto_play",false)&&NetWorkUtil.isWifiConnected(InitApp.AppContext);
        }
    
        /**
         * 获取字体大小
         * @return
         */
        public int getTextSize(){
            return setting.getInt("textsize",16);
        }
    
        /**
         * 设置字体大小
         * @param textSize
         */
        public void setTextSize(int textSize){
            setting.edit().putInt("textsize",textSize).apply();
        }
    
        public boolean getIsFirstTime(){
            return setting.getBoolean("first_time",true);
        }
    
        public void setIsFirstTime(boolean flag){
            setting.edit().putBoolean("first_time",flag).apply();
        }
    }
    View Code

    2.11.设置底部导航文字不消失

    package com.jasonjan.headnews.widget;
    
    import android.support.design.internal.BottomNavigationItemView;
    import android.support.design.internal.BottomNavigationMenuView;
    import android.support.design.widget.BottomNavigationView;
    import android.util.Log;
    
    import java.lang.reflect.Field;
    
    /**
     * Created by JasonJan on 2017/12/1.
     */
    
    public class BottomNavigationViewHelper {
        public static void disableShiftMode(BottomNavigationView view) {
            BottomNavigationMenuView menuView = (BottomNavigationMenuView) view.getChildAt(0);
            try {
                Field shiftingMode = menuView.getClass().getDeclaredField("mShiftingMode");
                shiftingMode.setAccessible(true);
                shiftingMode.setBoolean(menuView, false);
                shiftingMode.setAccessible(false);
                for (int i = 0; i < menuView.getChildCount(); i++) {
                    BottomNavigationItemView item = (BottomNavigationItemView) menuView.getChildAt(i);
                    //noinspection RestrictedApi
                    item.setShiftingMode(false);
                    // set once again checked value, so view will be updated
                    //noinspection RestrictedApi
                    item.setChecked(item.getItemData().isChecked());
                }
            } catch (NoSuchFieldException e) {
                Log.e("BNVHelper", "Unable to get shift mode field", e);
            } catch (IllegalAccessException e) {
                Log.e("BNVHelper", "Unable to change value of shift mode", e);
            }
        }
    }
    View Code


    3.子片段

    3.1.NewsTabLayout新闻片段

    package com.jasonjan.headnews.module.news;
    
    import android.os.Bundle;
    import android.support.design.widget.TabLayout;
    import android.support.v4.app.Fragment;
    import android.support.v4.view.ViewPager;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.ImageView;
    import android.widget.LinearLayout;
    
    import com.jasonjan.headnews.R;
    import com.jasonjan.headnews.module.base.BasePagerAdapter;
    import com.jasonjan.headnews.test.Test_Fragment;
    import com.jasonjan.headnews.util.SettingUtil;
    
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    import android.support.annotation.Nullable;
    
    import io.reactivex.Observable;
    
    
    /**
     * Created by JasonJan on 2017/12/1.
     */
    
    public class NewsTabLayout extends Fragment {
         public static final String TAG="NewsTabLayout";
         private static NewsTabLayout instance=null;
         private ViewPager viewPager;
         private BasePagerAdapter adapter;
         private LinearLayout linearLayout;
         //private NewsChannelDao dao=new NewsChannelDao();
         private List<Fragment> fragmentList;
         private List<String> titleList;
         private Observable<Boolean> observable;
         private Map<String,Fragment> map=new HashMap<>();
    
         public static NewsTabLayout getInstance(){
             if(instance==null){
                 instance=new NewsTabLayout();
             }
             return instance;
         }
    
        @Nullable
        @Override
        public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            View view = inflater.inflate(R.layout.fragment_news_tab, container, false);
            initView(view);
            initData();
            return view;
        }
    
        @Override
        public void onResume() {
            super.onResume();
            linearLayout.setBackgroundColor(SettingUtil.getInstance().getColor());
        }
    
         public void initView(View view){
             TabLayout tab_layout=view.findViewById(R.id.tab_layout_news);
             viewPager=view.findViewById(R.id.view_pager_news);
    
             tab_layout.setupWithViewPager(viewPager);
             tab_layout.setTabMode(TabLayout.MODE_SCROLLABLE);
             ImageView add_channel_iv=view.findViewById(R.id.add_chnnel_iv);
             add_channel_iv.setOnClickListener(new View.OnClickListener(){
                 @Override
                 public void onClick(View v){
                     //设置点击事件
                 }
             });
             linearLayout=view.findViewById(R.id.header_layout);
             linearLayout.setBackgroundColor(SettingUtil.getInstance().getColor());
         }
    
         public void initData(){
             fragmentList = new ArrayList<>();
             titleList = new ArrayList<>();
             fragmentList.add(new Test_Fragment());
             titleList.add("推荐");
             fragmentList.add(new Test_Fragment());
             titleList.add("热点");
             fragmentList.add(new Test_Fragment());
             titleList.add("视频");
             fragmentList.add(new Test_Fragment());
             titleList.add("社会");
             fragmentList.add(new Test_Fragment());
             titleList.add("娱乐");
    
             adapter = new BasePagerAdapter(getChildFragmentManager(), fragmentList, titleList);
             viewPager.setAdapter(adapter);
             viewPager.setOffscreenPageLimit(15);
    
         }
    }
    View Code

    3.2.NewsTabLayout布局代码

    <?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">
    
        <LinearLayout
            android:id="@+id/header_layout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
    
            <android.support.design.widget.TabLayout
                android:id="@+id/tab_layout_news"
                style="@style/TabLayout"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:minHeight="?attr/actionBarSize"
                android:theme="@style/AppTheme.AppBarOverlay"
                app:tabTextColor="@color/gray"
                />
    
            <ImageView
                android:id="@+id/add_chnnel_iv"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:layout_gravity="center"
                android:background="?attr/selectableItemBackground"
                android:foreground="?attr/selectableItemBackground"
                android:maxHeight="?attr/actionBarSize"
                android:paddingBottom="4dp"
                android:paddingLeft="10dp"
                android:paddingRight="10dp"
                android:paddingTop="4dp"
                android:scaleType="center"
                app:srcCompat="@drawable/ic_add_white_24dp"
                tools:ignore="ContentDescription"
                />
    
        </LinearLayout>
    
        <android.support.v4.view.ViewPager
            android:id="@+id/view_pager_news"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior"
            tools:layout="@layout/fragment_list">
        </android.support.v4.view.ViewPager>
    
    </LinearLayout>
    View Code

    3.3.PhotoTabLayout图片片段

    package com.jasonjan.headnews.module.photo;
    
    import android.os.Bundle;
    import android.support.annotation.Nullable;
    import android.support.design.widget.TabLayout;
    import android.support.v4.app.Fragment;
    import android.support.v4.view.ViewPager;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    
    import com.jasonjan.headnews.R;
    import com.jasonjan.headnews.global.InitApp;
    import com.jasonjan.headnews.module.base.BasePagerAdapter;
    import com.jasonjan.headnews.test.Test_Fragment;
    import com.jasonjan.headnews.util.SettingUtil;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * Created by JasonJan on 2017/12/1.
     */
    
    public class PhotoTabLayout extends Fragment {
        private static final String TAG = "PhotoTabLayout";
        private static PhotoTabLayout instance = null;
        private static int pageSize = InitApp.AppContext.getResources().getStringArray(R.array.photo_id).length;
        private String categoryId[] = InitApp.AppContext.getResources().getStringArray(R.array.photo_id);
        private String categoryName[] = InitApp.AppContext.getResources().getStringArray(R.array.photo_name);
        private TabLayout tabLayout;
        private ViewPager viewPager;
        private List<Fragment> fragmentList = new ArrayList<>();
        private BasePagerAdapter adapter;
    
        public static PhotoTabLayout getInstance() {
            if (instance == null) {
                instance = new PhotoTabLayout();
            }
            return instance;
        }
    
        @Nullable
        @Override
        public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            View view = inflater.inflate(R.layout.fragment_photo_tab, container, false);
            initView(view);
            initData();
            return view;
        }
    
        @Override
        public void onResume() {
            super.onResume();
            tabLayout.setBackgroundColor(SettingUtil.getInstance().getColor());
        }
    
    
    
        private void initView(View view){
            tabLayout = view.findViewById(R.id.tab_layout_photo);
            viewPager = view.findViewById(R.id.view_pager_photo);
    
            tabLayout.setupWithViewPager(viewPager);
            tabLayout.setTabMode(TabLayout.MODE_SCROLLABLE);
            tabLayout.setBackgroundColor(SettingUtil.getInstance().getColor());
            viewPager.setOffscreenPageLimit(pageSize);
        }
    
        private void initData(){
            for (int i = 0; i < categoryId.length; i++) {
                Fragment fragment = new Test_Fragment();//这里需要更改
                fragmentList.add(fragment);
            }
            adapter = new BasePagerAdapter(getChildFragmentManager(), fragmentList, categoryName);
            viewPager.setAdapter(adapter);
        }
    
        @Override
        public void onDestroy() {
            if (instance != null) {
                instance = null;
            }
            super.onDestroy();
        }
    }
    View Code

    3.4.PhotoTabLayout布局代码

    <?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">
    
        <android.support.design.widget.TabLayout
            android:id="@+id/tab_layout_photo"
            style="@style/TabLayout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:minHeight="?attr/actionBarSize"
            android:theme="@style/AppTheme.AppBarOverlay"
            app:tabTextColor="@color/gray">
        </android.support.design.widget.TabLayout>
    
        <android.support.v4.view.ViewPager
            android:id="@+id/view_pager_photo"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior"
            tools:layout="@layout/fragment_list">
        </android.support.v4.view.ViewPager>
    
    </LinearLayout>
    View Code

    3.5.VideoTabLayout视频片段

    package com.jasonjan.headnews.module.video;
    
    import android.os.Bundle;
    import android.support.annotation.Nullable;
    import android.support.design.widget.TabLayout;
    import android.support.v4.app.Fragment;
    import android.support.v4.view.ViewPager;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    
    import com.jasonjan.headnews.R;
    import com.jasonjan.headnews.global.InitApp;
    import com.jasonjan.headnews.module.base.BasePagerAdapter;
    import com.jasonjan.headnews.test.Test_Fragment;
    import com.jasonjan.headnews.util.SettingUtil;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * Created by JasonJan on 2017/12/1.
     */
    
    public class VideoTabLayout extends Fragment {
        private static VideoTabLayout instance = null;
        private static int pageSize = InitApp.AppContext.getResources().getStringArray(R.array.mobile_video_id).length;
        private String categoryId[] = InitApp.AppContext.getResources().getStringArray(R.array.mobile_video_id);
        private String categoryName[] = InitApp.AppContext.getResources().getStringArray(R.array.mobile_video_name);
        private TabLayout tabLayout;
        private ViewPager viewPager;
        private List<Fragment> fragmentList = new ArrayList<>();
        private BasePagerAdapter adapter;
    
        public static VideoTabLayout getInstance() {
            if (instance == null) {
                instance = new VideoTabLayout();
            }
            return instance;
        }
    
        @Nullable
        @Override
        public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            View view = inflater.inflate(R.layout.fragment_video_tab, container, false);
            initView(view);
            initData();
            return view;
        }
    
        @Override
        public void onResume() {
            super.onResume();
            tabLayout.setBackgroundColor(SettingUtil.getInstance().getColor());
        }
    
        private void initView(View view) {
            tabLayout = view.findViewById(R.id.tab_layout_video);
            viewPager = view.findViewById(R.id.view_pager_video);
    
            tabLayout.setupWithViewPager(viewPager);
            tabLayout.setTabMode(TabLayout.MODE_SCROLLABLE);
            tabLayout.setBackgroundColor(SettingUtil.getInstance().getColor());
            viewPager.setOffscreenPageLimit(pageSize);
        }
    
        private void initData() {
            for (int i = 0; i < categoryId.length; i++) {
                Fragment fragment = new Test_Fragment();//这里需要更改
                fragmentList.add(fragment);
            }
            adapter = new BasePagerAdapter(getChildFragmentManager(), fragmentList, categoryName);
            viewPager.setAdapter(adapter);
        }
    
        @Override
        public void onDestroy() {
            if (instance != null) {
                instance = null;
            }
            super.onDestroy();
        }
    }
    View Code

    3.6.VideoTabLayout布局代码

    <?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">
    
        <android.support.design.widget.TabLayout
            android:id="@+id/tab_layout_video"
            style="@style/TabLayout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:minHeight="?attr/actionBarSize"
            android:theme="@style/AppTheme.AppBarOverlay"
            app:tabTextColor="@color/gray">
        </android.support.design.widget.TabLayout>
    
        <android.support.v4.view.ViewPager
            android:id="@+id/view_pager_video"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior"
            tools:layout="@layout/fragment_list">
        </android.support.v4.view.ViewPager>
    
    </LinearLayout>
    View Code

    3.7.整理片段的适配器

    package com.jasonjan.headnews.module.base;
    
    import android.support.v4.app.Fragment;
    import android.support.v4.app.FragmentManager;
    import android.support.v4.app.FragmentStatePagerAdapter;
    import android.support.v4.view.PagerAdapter;
    
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    
    /**
     * Created by JasonJan on 2017/12/3.
     */
    
    public class BasePagerAdapter extends FragmentStatePagerAdapter {
        private List<Fragment> fragmentList;
        private List<String> titleList;
    
        public BasePagerAdapter(FragmentManager fm, List<Fragment> fragmentList, String[] titles) {
            super(fm);
            this.fragmentList = fragmentList;
            this.titleList = new ArrayList<>(Arrays.asList(titles));
        }
    
        public BasePagerAdapter(FragmentManager fm, List<Fragment> fragmentList, List<String> titleList) {
            super(fm);
            this.fragmentList = fragmentList;
            this.titleList = titleList;
        }
    
        @Override
        public Fragment getItem(int position) {
            return fragmentList.get(position);
        }
    
        @Override
        public int getCount() {
            return titleList.size();
        }
    
        @Override
        public CharSequence getPageTitle(int position) {
            return titleList.get(position);
        }
    
        @Override
        public int getItemPosition(Object object) {
            return PagerAdapter.POSITION_NONE;
        }
    
        //刷新一下整个List
        public void recreateItems(List<Fragment> fragmentList,List<String> titleList){
            this.fragmentList=fragmentList;
            this.titleList=titleList;
            notifyDataSetChanged();
        }
    
    }
    View Code

    3.8.测试片段代码

    package com.jasonjan.headnews.test;
    
    import android.os.Bundle;
    import android.support.annotation.Nullable;
    import android.support.v4.app.Fragment;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    
    
    import com.jasonjan.headnews.R;
    
    /**
     * Created by JasonJan on 2017/12/3.
     */
    
    public class Test_Fragment extends Fragment {
    
            @Nullable
            @Override
            public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
                View view = inflater.inflate(R.layout.test_fragment, container, false);
                return view;
            }
    
    
    }
    View Code

    3.9.测试片段布局代码

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:background="@color/Grey50"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
       <TextView
           android:id="@+id/test_fragment_txt1"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="这是..."
           android:layout_centerInParent="true"
           android:textSize="50dp"
           />
    
    </RelativeLayout>
    View Code


    4.一些资源文件

    4.1.底部导航栏菜单样式

    <?xml version="1.0" encoding="utf-8"?>
    <menu xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto">
    
        <item
            android:id="@+id/action_news"
            android:enabled="true"
            android:icon="@drawable/ic_newspaper_white_24dp"
            android:title="@string/title_news"
            app:showAsAction="ifRoom"/>
        <item
            android:id="@+id/action_photo"
            android:enabled="true"
            android:icon="@drawable/ic_gallery_white_24dp"
            android:title="@string/title_photo"
            app:showAsAction="ifRoom"/>
        <item
            android:id="@+id/action_video"
            android:enabled="true"
            android:icon="@drawable/ic_youtube_white_24dp"
            android:title="@string/title_video"
            app:showAsAction="ifRoom"/>
        <item
            android:id="@+id/action_media"
            android:enabled="true"
            android:icon="@drawable/ic_library_books_white_24dp"
            android:title="@string/title_media"
            app:showAsAction="ifRoom"/>
    
    </menu>

    4.2.顶部搜索菜单布局

    <?xml version="1.0" encoding="utf-8"?>
    <menu xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto">
    
        <item
            android:id="@+id/action_search"
            android:icon="@drawable/ic_search_white_24dp"
            android:title="@string/action_search"
            app:showAsAction="always|collapseActionView"/>
    
    </menu>

    4.3.侧边栏菜单布局

    <?xml version="1.0" encoding="utf-8"?>
    <menu xmlns:android="http://schemas.android.com/apk/res/android">
    
        <group android:id="@+id/other">
            <item
                android:id="@+id/nav_switch_night_mode"
                android:icon="@drawable/ic_night_gray_24dp"
                android:title="@string/nav_switch_night_mode"/>
            <item
                android:id="@+id/nav_setting"
                android:icon="@drawable/ic_setting_gray_24dp"
                android:title="@string/nav_setting"/>
            <item
                android:id="@+id/nav_share"
                android:icon="@drawable/ic_share_grey_24dp"
                android:title="@string/nav_share"/>
        </group>
    
    </menu>

    4.4.颜色资源文件==>color.xml

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
            <color name="colorPrimary">#E91E63</color>
            <color name="colorPrimaryDark">#C2185B</color>
            <color name="colorAccent">#FF5252</color>
            <color name="textColorPrimary">#616161</color>
            <color name="viewBackground">@android:color/white</color>
            <color name="windowBackground">@color/Grey100</color>
            <color name="line_divider">@color/Grey300</color>
    
            <color name="colorPrimary_night">#35464e</color>
            <color name="colorPrimaryDark_night">#212a2f</color>
            <color name="colorAccent_night">#212a2f</color>
            <color name="textColorPrimary_night">#616161</color>
            <color name="viewBackground_night">#212a2f</color>
    
            <color name="White">#FFFFFF</color>
            <color name="Black">#000000</color>
    
            <color name="Red">#f44336</color>
            <color name="Pink">#e91e63</color>
            <color name="Purple">#9c27b0</color>
            <color name="Deep_Purple">#673ab7</color>
            <color name="Indigo">#3f51b5</color>
            <color name="Blue">#2196f3</color>
            <color name="Light_Blue">#03a9f4</color>
            <color name="Cyan">#00bcd4</color>
            <color name="Teal">#009688</color>
            <color name="Green">#4caf50</color>
            <color name="Light_Green">#8bc34a</color>
            <color name="Lime">#cddc39</color>
            <color name="Yellow">#ffeb3b</color>
            <color name="Amber">#ffc107</color>
            <color name="Orange">#ff9800</color>
            <color name="Deep_Orange">#ff5722</color>
            <color name="Brown">#795548</color>
            <color name="Grey">#9e9e9e</color>
            <color name="Blue_Grey">#607d8b</color>
    
    
            <color name="gray">#9cffffff</color>
            <color name="dkgray">#8f8989</color>
            <color name="NULL">#00FFFFFF</color>
    
            <color name="shadow_material_light">#727272</color>
    
            <color name="Grey50">#FAFAFA</color>
            <color name="Grey100">#F5F5F5</color>
            <color name="Grey200">#EEEEEE</color>
            <color name="Grey300">#E0E0E0</color>
            <color name="Grey400">#bdbdbd</color>
            <color name="Grey500">#9e9e9e</color>
            <color name="Grey600">#757575</color>
            <color name="Grey700">#616161</color>
            <color name="Grey800">#424242</color>
            <color name="Grey900">#212121</color>
    </resources>
    View Code

    4.5.type分类==>视频类型分类==>mobile_video_category.xml

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
    
        <string-array name="mobile_video_name">
            <item>推荐</item>
            <item>音乐</item>
            <item>搞笑</item>
            <item>社会</item>
            <item>小品</item>
    
            <item>生活</item>
            <item>影视</item>
            <item>娱乐</item>
            <item>呆萌</item>
            <item>游戏</item>
    
            <item>原创</item>
            <item>开眼</item>
    
        </string-array>
    
        <string-array name="mobile_video_id">
            <item>video</item>
            <item>subv_voice</item>
            <item>subv_funny</item>
            <item>subv_society</item>
            <item>subv_comedy</item>
    
            <item>subv_life</item>
            <item>subv_movie</item>
            <item>subv_entertainment</item>
            <item>subv_cute</item>
            <item>subv_game</item>
    
            <item>subv_boutique</item>
            <item>subv_broaden_view</item>
    
        </string-array>
    
    </resources>
    View Code

    4.6.图片类型分类==>photo_category.xml

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <string-array name="photo_name">
            <item>全部</item>
            <item>老照片</item>
            <item>故事照片</item>
            <item>摄影集</item>
        </string-array>
    
        <string-array name="photo_id">
            <item>组图</item>
            <item>gallery_old_picture</item>
            <item>gallery_story</item>
            <item>gallery_photograthy</item>
        </string-array>
    </resources>
    View Code

    4.7.styles.xml

    <resources>
    
        <!-- Base application theme. -->
        <style name="AppTheme" parent="Theme.AppCompat.DayNight.DarkActionBar">
             <!--Customize your theme here. -->
            <item name="colorPrimary">@color/colorPrimary</item>
            <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
            <item name="colorAccent">@color/colorAccent</item>
        </style>
    
        <style name="AppTheme.NoActionBar">
            <item name="windowActionBar">false</item>
            <item name="windowNoTitle">true</item>
        </style>
    
        <style name="SplashTheme" parent="AppTheme.NoActionBar">
            <item name="android:windowBackground">@drawable/bg_splash</item>
        </style>
    
        <style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar"/>
    
        <style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light"/>
    
        <style name="TabLayout" parent="Base.Widget.Design.TabLayout">
            <item name="tabMaxWidth">@dimen/design_tab_max_width</item>
            <item name="tabIndicatorColor">#FFFFFF</item>
            <item name="tabIndicatorHeight">2dp</item>
            <item name="tabPaddingStart">12dp</item>
            <item name="tabPaddingEnd">12dp</item>
            <item name="tabBackground">?attr/selectableItemBackground</item>
            <item name="tabTextAppearance">@style/TextAppearance.Design.Tab</item>
            <item name="tabSelectedTextColor">?android:textColorPrimary</item>
        </style>
    
    </resources>
    View Code

    4.8.清单资源文件==>AndroidManifest.xml

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.jasonjan.headnews">
    
        <uses-permission android:name="android.permission.INTERNET"/>
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
        <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
        <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
        <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
        <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
    
        <uses-permission-sdk-23 android:name="android.permission.INTERNET"/>
        <uses-permission-sdk-23 android:name="android.permission.READ_EXTERNAL_STORAGE"/>
        <uses-permission-sdk-23 android:name="android.permission.ACCESS_NETWORK_STATE"/>
        <uses-permission-sdk-23 android:name="android.permission.ACCESS_WIFI_STATE"/>
    
        <application
            android:name=".global.InitApp"
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher_circle"
            android:label="@string/app_name"
            android:supportsRtl="true"
            android:theme="@style/AppTheme">
            <activity
                android:name=".global.SplashActivity"
                android:configChanges="orientation|screenSize|uiMode"
                android:label="@string/app_name"
                android:theme="@style/SplashTheme"
                >
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                </intent-filter>
            </activity>
    
            <activity-alias
                android:name=".global.SplashActivity_circle"
                android:label="@string/app_name"
                android:enabled="true"
                android:icon="@mipmap/ic_launcher_circle"
                android:targetActivity=".global.SplashActivity"
                >
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity-alias>
    
            <activity-alias
                android:name=".global.SplashActivity_rect"
                android:label="@string/app_name"
                android:enabled="false"
                android:icon="@mipmap/ic_launcher_rect"
                android:targetActivity=".global.SplashActivity"
                >
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity-alias>
    
            <activity-alias
                android:name=".global.SplashActivity_square"
                android:label="@string/app_name"
                android:enabled="false"
                android:icon="@mipmap/ic_launcher_square"
                android:targetActivity=".global.SplashActivity"
                >
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity-alias>
    
            <activity android:name=".main.MainActivity"
                android:configChanges="orientation|screenSize|uiMode"
                android:label="@string/app_name"
                android:theme="@style/AppTheme.NoActionBar"
                />
    
        </application>
    
    </manifest>
    View Code

    4.9.build.gradle

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.jasonjan.headnews">
    
        <uses-permission android:name="android.permission.INTERNET"/>
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
        <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
        <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
        <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
        <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
    
        <uses-permission-sdk-23 android:name="android.permission.INTERNET"/>
        <uses-permission-sdk-23 android:name="android.permission.READ_EXTERNAL_STORAGE"/>
        <uses-permission-sdk-23 android:name="android.permission.ACCESS_NETWORK_STATE"/>
        <uses-permission-sdk-23 android:name="android.permission.ACCESS_WIFI_STATE"/>
    
        <application
            android:name=".global.InitApp"
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher_circle"
            android:label="@string/app_name"
            android:supportsRtl="true"
            android:theme="@style/AppTheme">
            <activity
                android:name=".global.SplashActivity"
                android:configChanges="orientation|screenSize|uiMode"
                android:label="@string/app_name"
                android:theme="@style/SplashTheme"
                >
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                </intent-filter>
            </activity>
    
            <activity-alias
                android:name=".global.SplashActivity_circle"
                android:label="@string/app_name"
                android:enabled="true"
                android:icon="@mipmap/ic_launcher_circle"
                android:targetActivity=".global.SplashActivity"
                >
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity-alias>
    
            <activity-alias
                android:name=".global.SplashActivity_rect"
                android:label="@string/app_name"
                android:enabled="false"
                android:icon="@mipmap/ic_launcher_rect"
                android:targetActivity=".global.SplashActivity"
                >
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity-alias>
    
            <activity-alias
                android:name=".global.SplashActivity_square"
                android:label="@string/app_name"
                android:enabled="false"
                android:icon="@mipmap/ic_launcher_square"
                android:targetActivity=".global.SplashActivity"
                >
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity-alias>
    
            <activity android:name=".main.MainActivity"
                android:configChanges="orientation|screenSize|uiMode"
                android:label="@string/app_name"
                android:theme="@style/AppTheme.NoActionBar"
                />
    
        </application>
    
    </manifest>
    View Code


    5.项目下载

    链接: https://pan.baidu.com/s/1o8sqTuU 
    密码: tvw2


    既然选择了,便不顾风雨兼程。Just follow yourself.
  • 相关阅读:
    2019春第九周作业
    2019春第八周作业
    2019春第七周作业
    2019春第六周作业
    2019春第五周作业
    2019年春季学期第四周作业
    2019年春季学期第三周作业
    2019年春季学期第二周作业
    学习进度条
    第六周作业
  • 原文地址:https://www.cnblogs.com/Jason-Jan/p/7965768.html
Copyright © 2020-2023  润新知