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,"点击这里切换新闻\n再次点击刷新当前页面")
.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()
}
} }
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>
@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>
@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>
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);
}
} }
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;
}
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;
}
}
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
} }
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;
} }
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();
}
}
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);
}
}
}
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); }
}
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>
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();
}
}
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>
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();
}
}
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>
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();
} }
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;
} }
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>
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>
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>
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>
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>
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>
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>
5.项目下载
链接: https://pan.baidu.com/s/1o8sqTuU
密码: tvw2
TouTiao开源项目 分析笔记4==>一个简单APP 整体常用框架的更多相关文章
- TouTiao开源项目 分析笔记2
1.Constant常量定义类 1.1.源代码 public class Constant { public static final String USER_AGENT_MOBILE = " ...
- TouTiao开源项目 分析笔记13 最后一个订阅号的实现主页面
1.实现订阅号的基础类 1.1.本地订阅号的Bean类==>MediaChannelBean public class MediaChannelBean implements Parcelabl ...
- TouTiao开源项目 分析笔记9 实现一个问答主页面
1.根据API返回创建几个基础的Bean 1.1.WendaArticleDataBean类 API返回的数据如下: /** * cell_type : 36 * extra : {"wen ...
- TouTiao开源项目 分析笔记6
1.NewsChannelBean简单类笔记 1.1.Comparable接口的实现和使用 参考文章:Comparable接口的实现和使用. 因为NewsChannelBean实现了Comparabl ...
- TouTiao开源项目 分析笔记15 新闻详情之两种类型的实现
1.预览效果 1.1.首先看一下需要实现的效果. 第一种,文字类型新闻. 第二种,图片类型新闻. 1.2.在NewsArticleTextViewBinder中设置了点击事件 RxView.click ...
- TouTiao开源项目 分析笔记10 实现通用普通文章片段页面
1.RxJava的Observable数据操作符总结 1.1.Map操作符 Map操作符对原始Observable发射的没一项数据应用一个你选择的函数, 然后返回一个发射这些结果的Observable ...
- TouTiao开源项目 分析笔记1
1.InitApp==>项目的入口Application 1.1.继承了MultiDexApplication 超过65K方法的APP,会遇到65535的错误.原因就是为了支持比较大型的APP而 ...
- TouTiao开源项目 分析笔记17 新闻媒体专栏
1.效果预览 1.1.要实现的效果 1.2.如何调转到新闻媒体专栏 点击右上角的用户图标. 在新闻详情页面的Fragment的菜单点击事件中触发. case R.id.action_open_medi ...
- TouTiao开源项目 分析笔记14 段子评论
1.段子页面详情 1.1.先看看预览界面吧 左边的页面已经实现了,现在的目的就是要实现点击左侧的每一个item 然后跳转到右边相应的段子详情页面. 1.2.首先肯定有右侧这个活动==>JokeC ...
随机推荐
- Android5.0以后,materialDesign风格的加阴影和裁剪效果
5.0以后,materialDesign风格,出现了立体这种概念,高光,阴影,也就是Z轴,凸显层次:同时,裁剪view也变得方便简单了很多. 1,先说说阴影的实现. 方案1:在xml中设置 xml中设 ...
- centos6.5 网卡配置
家里用的电脑是电信拨号的 所以用不了桥接模式 改用nat vi /etc/sysconfig/network-scripts/ifcfg-eth0 DEVICE="eth0" HW ...
- 动软代码生成器,主子表增加的时候子表的parentID无法插入问题解决方案
StringBuilder strSql=new StringBuilder(); strSql.Append("insert into HT_XunJiaMain("); str ...
- 实现vmare虚拟机系统随主机开机自动启动
服务器主机上的虚拟机每次开机要手动启动是很麻烦的事,so,在网上找到一方法让虚拟机随主机开机自动运行:挺方便的,记录下来: 1.操作环境 主机:windows 2003 虚拟机:centos6 2.下 ...
- 16 Javascript
网上找网页模板: 1.HTML模板 2.BootStrap 前面内容总结 HTML 标签:块级,行内 CSS 后台管理布局 position: fixed ---永远固定在窗口的某个位置 relat ...
- socks v5 协议解析
socks v5是一种用于代理的协议,就是说client用这种协议与server沟通,让server帮忙代访问remote后再将结果通过此协议返给client,所以一般是涉及到3个端,分别是clien ...
- 分治——sqtx
题目描述 Given a string s, partition s such that every substring of the partition is a palindrome. Retur ...
- Git-实验报告
“Git 实战教程”实验报告 基本用法(下) 二.比较内容 1.比较提交 - Git Diff git diff命令的作用是比较修改的或提交的文件内容. 如何查看缓存区内与上次提交之间的差别呢?需要使 ...
- Vue--父组件传数据给子组件,子组件生命周期过程拿到数据的情况
需求: 在子组件渲染之前,我要修改数据的某个字段 结果是 组件在beforeUpdate,updated 的状态才能拿到父组件的数据 那么证明,我根本无法在beforeUpdate,updated两个 ...
- win10启动项添加方法
1.添加或删除启动文件夹下的快捷方式实现开机自启动 我们可以直接将应用软件的快捷方式拖到启动文件夹里,下次开机时便会自动运行这些软件. 不需要开机启动某些软件了就将启动文件夹里的该软件的快捷方式删除掉 ...