diff --git "a/Android/Blog/AndroidAdvancedSkill/Android-\345\256\236\347\216\260Animation everywhere.md" "b/Android/Blog/AndroidAdvancedSkill/Android-\345\256\236\347\216\260Animation everywhere.md"
new file mode 100644
index 0000000..bcb0a2e
--- /dev/null
+++ "b/Android/Blog/AndroidAdvancedSkill/Android-\345\256\236\347\216\260Animation everywhere.md"
@@ -0,0 +1,2 @@
+# Android-实现Animation everywhere
+[参考项目](http://mp.weixin.qq.com/s/F1MRwUgCQW8d3hWhcefFtg)
diff --git "a/Android/Blog/AndroidAdvancedSkill/MVP\345\256\236\346\210\230.md" "b/Android/Blog/AndroidAdvancedSkill/MVP\345\256\236\346\210\230.md"
new file mode 100644
index 0000000..973f98a
--- /dev/null
+++ "b/Android/Blog/AndroidAdvancedSkill/MVP\345\256\236\346\210\230.md"
@@ -0,0 +1,2 @@
+# MVP实战
+[参考项目](http://www.jianshu.com/p/b1df2a42a380)
diff --git a/Android/Blog/AndroidAdvancedSkill/README.md b/Android/Blog/AndroidAdvancedSkill/README.md
index 3ee5aa0..8dacd41 100644
--- a/Android/Blog/AndroidAdvancedSkill/README.md
+++ b/Android/Blog/AndroidAdvancedSkill/README.md
@@ -1 +1,2 @@
# Android 高级技巧
+* 完全推出App实战
diff --git "a/Android/Blog/AndroidAdvancedSkill/\345\256\214\345\205\250\346\216\250\345\207\272App\345\256\236\346\210\230.md" "b/Android/Blog/AndroidAdvancedSkill/\345\256\214\345\205\250\346\216\250\345\207\272App\345\256\236\346\210\230.md"
new file mode 100644
index 0000000..13104e9
--- /dev/null
+++ "b/Android/Blog/AndroidAdvancedSkill/\345\256\214\345\205\250\346\216\250\345\207\272App\345\256\236\346\210\230.md"
@@ -0,0 +1,31 @@
+# 用EventBus实现完全推出App实战
+## 背景
+通过简单的使用`EventBus`来实现完全退出一个`App`,通过`EventBus`的事件通知机制,在需要退出的时候发送消息,就不用通过`Activity`自定义栈来管理和实现。(别人已实现,自己写一遍,纪录一下加深印象)
+## 具体实现
+* `AppEventApp`
+首先定义一个`AppEventApp`类,用`EventBus`事件传输
+* `BaseActivity`
+其次定义一个`BaseActivity`类,继承于`Activity`在其`onCreate()`方法中调用:
+```
+EventBus.getDefault().register(this);
+```
+在`onDestroy()`方法中调用:
+```
+EventBus.getDefault().unregister(this);
+```
+同时实现事件接收的方法:
+```
+@Subscribe(threadMode = ThreadMode.MAIN)
+public void onMessageEvent(AppExitEvent event) {
+ finish();
+ System.exit(0);
+}
+```
+这个方法主要是接收消息后去退出`Acitivity`
+* 最后让每个`Activity`去继承于`BaseActivity`,在需要退出的地方调用以下代码:
+```
+EventBus.getDefault().post(new AppExitEvent());
+```
+
+## 感悟
+在接收到退出消息之前其实还是可以做很多事情,比如发个广播等。
diff --git "a/Android/Blog/AndroidBaseSkill/Android\345\261\217\345\271\225\351\200\202\351\205\215\345\217\212DisplayMetrics\350\247\243\346\236\220.md" "b/Android/Blog/AndroidBaseSkill/Android\345\261\217\345\271\225\351\200\202\351\205\215\345\217\212DisplayMetrics\350\247\243\346\236\220.md"
new file mode 100644
index 0000000..8e6a516
--- /dev/null
+++ "b/Android/Blog/AndroidBaseSkill/Android\345\261\217\345\271\225\351\200\202\351\205\215\345\217\212DisplayMetrics\350\247\243\346\236\220.md"
@@ -0,0 +1,157 @@
+# Android屏幕适配及DisplayMetrics解析
+[参考文章](http://blog.csdn.net/hp910315/article/details/48501197)
+# 背景
+在自己的项目开发中一直用的都是别人写好的屏幕尺寸单位转换函数,都没有自己去专门看看其中的原理,以及各个单位之间的区别及用法,其实在上面的参考文章中已经写的很好了,我这里相当于记录一下,自己写一遍加深理解并加入自己的理解。
+# 基本概念
+* 屏幕尺寸
+屏幕尺寸指屏幕的对角线的长度,单位是英寸(`in`),`1`英寸=`2.54`厘米
+* px
+是英文单词`pixel`的缩写,意为像素,屏幕上的点。我们通常所说的分辨率如`480X800`就是指的像素,一般以纵向像素`*`横向像素。
+* dpi
+`dpi`是`Dots Per Inch`的缩写, 每英寸点数,即每英寸包含像素个数。
+* density
+屏幕密度,`density`和`dpi`的关系为 `density = dpi/160`
+* dp和dip
+设备独立像素,`device independent pixels`的缩写,`Android`特有的单位,在屏幕密度`dpi = 160`屏幕上,`1dp = 1px`
+* sp
+和`dp`很类似,一般用来设置字体大小,和`dp`的区别是它可以根据用户的字体大小偏好来缩放。
+#### 问题
+ * 为什么我们需要推荐使用`dp`而不推荐使用`px`
+不使用`px`的原因是由于不同的手机分辨率是不同的,相同的像素对应的尺寸是不同的。
+推荐使用`dp`的原因是我们可以通过`dp`设置指定的尺寸,这个不受像素的影响,是像素无关的。从上面可以看出`dp`就是等于屏幕密度,从而可以得到`1dp`对应设备的像素,这样我们就可以设置真实的尺寸大小,这个大小跟像素是无关的。就是说`dp`能够让同一数值在不同的分辨率展示出大致相同的尺寸大小。
+ * 为什么dp还是不能解决屏幕适配的问题
+`dp`可以用来设置指定尺寸,这个不受像素的影响,这样对不同的手机我们可以指定相同的尺寸,但还有一个问题就是市面上的手机尺寸并不是相同的,常见的屏幕尺寸有`2.4、2.8、3.5、3.7、4.2、5.0、5.5、6.0`等,对于这么多尺寸,我们在为布局设置宽高的时候,的确可以使用dp来给定一个固定的大小,但是我们却不知道用户手机的尺寸是多少,如果设置尺寸太大,可能屏幕容纳不小,如果设置的尺寸太小,屏幕可能留出许多的空白区域。
+
+# Android Drawable适配
+* 普通drawable
+新建一个`Android`项目后应该可以看到很多`drawable`文件夹,分别对应不同的`dpi`
+ ```
+ drawable-ldpi (dpi=120, density=0.75)
+
+ drawable-mdpi (dpi=160, density=1)
+
+ drawable-hdpi (dpi=240, density=1.5)
+
+ drawable-xhdpi (dpi=320, density=2)
+
+ drawable-xxhdpi (dpi=480, density=3)
+ ```
+ 对于五种主流的像素密度`(MDPI、HDPI、XHDPI、XXHDPI 和 XXXHDPI)`应按照 `2:3:4:6:8` 的比例进行缩放。
+ 我们一般的做法是以高分辨率作为设计大小,然后按照倍数对应缩小到小分辨率的图片。因为小分辨率在生成高分辨率图片的时候,会出现像素丢失。
+
+# 布局的适配
+对于不同的屏幕,如果差别太大,我们就需要使用不同的布局文件,我们可以通过使用配置限定符,在运行时根据当前的设备配置自动选择合适的资源了,例如根据各种屏幕尺寸选择不同的布局。
+根据物理尺寸的大小准备`5`套布局:
+`layout`(放一些通用布局`xml`文件,比如界面顶部和底部的布局,不会随着屏幕大小变化,类似`windos`窗口的`title bar`)
+`layout-small`(屏幕尺寸小于3英寸左右的布局)
+`layout-normal`(屏幕尺寸小于4.5英寸左右)
+`layout-large`(4英寸-7英寸之间)
+`layout-xlarge`(7-10英寸之间)
+# 百分比布局
+这是最理想的一种布局方案,使用百分比就可以完全不用担心适配的问题。`android-percent-support`这个库
+用法可以看下面博客:[`Android` 百分比布局库``(percent-support-lib`) 解析与扩展;`Android` 增强版百分比布局库 为了适配而扩展](http://blog.csdn.net/lmj623565791/article/details/46695347)
+.
+另外,还有另外一种方案,根据一个基准,比如`480*320`的分辨率为基准,对各种不同手机的像素按照这个标准将垂直像素和水平像素分别均分成`480`份和`320`份。这样就可以得到每一份的像素,我们在设置的时候,直接使用份数就可以了,这也相当于是百分比,具体参照下面文章:[`Android` 屏幕适配方案](http://blog.csdn.net/lmj623565791/article/details/45460089)
+# 注意点
+* 使用wrap_content、match_parent、weight
+`weight`的计算方法:设置宽度(`android:layout_width`) + 剩余宽度的占位比
+* 使用相对布局,禁用绝对布局
+* 使用限定符
+* 使用自动拉伸位图
+
+# DisplayMetrics解析
+* 获取屏幕分辨率信息的三种方法
+```
+DisplayMetrics metrics = new DisplayMetrics();
+Display display = activity.getWindowManager().getDefaultDisplay();
+display.getMetrics(metrics);
+```
+```
+DisplayMetrics metrics=activity.getResources().getDisplayMetrics();
+```
+```
+Resources.getSystem().getDisplayMetrics();
+```
+* 上面所有的单位到px的转换
+`android.util.TypedValue`类提供了一个函数,支持把所有的单位换算到px,实现代码如下:
+```
+public static float applyDimension(int unit, float value,
+ DisplayMetrics metrics)
+{
+ switch (unit) {
+ //px:pixel
+ case COMPLEX_UNIT_PX:
+ return value;
+ //dp(dip)
+ case COMPLEX_UNIT_DIP:
+ return value * metrics.density;
+ //sp
+ case COMPLEX_UNIT_SP:
+ return value * metrics.scaledDensity;
+ //pt : 1/72英寸
+ case COMPLEX_UNIT_PT:
+ return value * metrics.xdpi * (1.0f/72);
+ //in: inch 英寸
+ case COMPLEX_UNIT_IN:
+ return value * metrics.xdpi;
+ //mm : 毫米 1英寸=25.4毫米
+ case COMPLEX_UNIT_MM:
+ return value * metrics.xdpi * (1.0f/25.4f);
+ }
+ return 0;
+}
+```
+ 从上面我们可以看到`dp、sp、pt、in、mm`转换为`px`的方法。
+
+ 在上面用到了`DisplayMetrics`对象,在这个对象中有哪些内容,我们来看看这个类。
+ ```
+ //设备的绝对宽度,单位是px
+ public int widthPixels;
+ //设备的绝对高度,单位是px
+ public int heightPixels;
+ //屏幕密度,它的计算方法在上面的概念中已经给出了
+ public float density;
+ //dpi 上面的概念已经给出,单位尺寸的像素点
+ public int densityDpi;
+ //字体显示的缩放因子,跟上面的density是一样
+ public float scaledDensity;
+ //水平方向的dpi
+ public float xdpi;
+ //竖直方向的dpi
+ public float ydpi;
+ ```
+* 运用
+ 在`TextView`里面有个`setTextSize`方法,它可以指定设置字体的单位。
+ ```
+ mTestText1 = (TextView) findViewById(R.id.test1);
+ mTestText1.setTextSize(TypeValue.COMPLEX_UNIT_SP, 18);
+ ```
+ 上面就是将TextView对象的字体设置为`18sp`.
+ 我们来看看它内部的实现源码:
+ ```
+ public void setTextSize(float size) {
+ setTextSize(TypedValue.COMPLEX_UNIT_SP, size);
+ }
+ public void setTextSize(int unit, float size) {
+ Context c = getContext();
+ Resources r;
+ if (c == null)
+ r = Resources.getSystem();
+ else
+ r = c.getResources();
+ setRawTextSize(TypedValue.applyDimension(
+ unit, size, r.getDisplayMetrics()));
+ }
+ ```
+ 从上面可以看到,它默认单位就是`TypedValue.COMPLEX_UNIT_SP`,最终调用的其实就是上面我们讲到的`TypedValue.applyDimension`方法,其实它就是把`sp`转成了`px`.
+
+# 相关文章
+[`Android`屏幕适配全攻略(最权威的官方适配指导)](http://blog.csdn.net/zhaokaiqiang1992/article/details/45419023)
+
+[`Android` 屏幕适配](http://blog.csdn.net/wangqing830414/article/details/26214959)
+
+[`android`屏幕适配详解](http://www.cnblogs.com/error404/p/3815739.html)
+
+# 官方链接
+
+[`Supporting Multiple Screens`](http://developer.android.com/guide/practices/screens_support.html)
diff --git "a/Android/Blog/AndroidBaseSkill/ListView\346\202\254\346\265\256\345\244\264\351\203\250\345\256\236\350\267\265.md" "b/Android/Blog/AndroidBaseSkill/ListView\346\202\254\346\265\256\345\244\264\351\203\250\345\256\236\350\267\265.md"
index bd4b5b7..aedbf02 100644
--- "a/Android/Blog/AndroidBaseSkill/ListView\346\202\254\346\265\256\345\244\264\351\203\250\345\256\236\350\267\265.md"
+++ "b/Android/Blog/AndroidBaseSkill/ListView\346\202\254\346\265\256\345\244\264\351\203\250\345\256\236\350\267\265.md"
@@ -1 +1,152 @@
-[项目地址](http://www.cnblogs.com/xqxacm/p/5642063.html)
+# `ListView`悬浮头部实现
+[参考项目](http://www.cnblogs.com/xqxacm/p/5642063.html)
+## 实现效果
+在添加头部的情况下,当`ListView`中的头部视图滑动到顶端时不消失,停留在页面的顶部,固定住。
+## 实现原理
+* `ListView`提供方法`addHeadView(View)`可以在`ListView`的头部添加一个视图,这个视图可以和`ListView`一起滚动
+* 用`FrameLayout`布局,在`ListView`的上方放一个和头部一样的视图,默认设置为不显示,当滑动时 `firstVisibleItem`>=要悬浮的`item`的`position`时,让在`ListView`上方的视图显示 ,否则隐藏
+
+## 实现代码
+#### 第一个头部的布局代码:
+```
+
+
+
+
+```
+#### 悬浮头部的布局代码:
+```
+
+
+
+
+
+
+```
+#### 整个布局代码:
+```
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+#### `Activity`里面的代码:
+```
+package com.example.hankcoder.completequitapp;
+
+import android.os.Bundle;
+import android.view.View;
+import android.widget.AbsListView;
+import android.widget.ArrayAdapter;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+
+import com.example.hankcoder.completequitapp.base.BaseActivity;
+
+public class MainActivity extends BaseActivity {
+
+ private LinearLayout mLlFalseHeader;
+ private ListView mLvContent;
+
+ private String[] mContent;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.linearlayout_listview);
+ generateData();
+ initView();
+ }
+
+ private void initView() {
+ mLlFalseHeader = (LinearLayout) findViewById(R.id.header_false);
+ mLvContent = (ListView) findViewById(R.id.listviews);
+ View header = View.inflate(this, R.layout.linearlayout_listview_header2, null);
+ mLvContent.addHeaderView(header);
+ mLvContent.addHeaderView(View.inflate(this, R.layout.linearlayout_listview_header, null));
+ mLvContent.setAdapter(new ArrayAdapter(this, android.R.layout.simple_list_item_1, mContent));
+ mLvContent.setOnScrollListener(new AbsListView.OnScrollListener() {
+ @Override
+ public void onScrollStateChanged(AbsListView view, int scrollState) {
+
+ }
+
+ @Override
+ public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
+ if (firstVisibleItem >= 1) {
+ mLlFalseHeader.setVisibility(View.VISIBLE);
+ } else {
+ mLlFalseHeader.setVisibility(View.GONE);
+ }
+ }
+ });
+ }
+
+ private void generateData() {
+ mContent = new String[100];
+ for (int index = 0; index < mContent.length; index++) {
+ mContent[index] = "data" + " " + index;
+ }
+ }
+}
+```
+
+## 感悟
+下次用这个原理来实现一个有很多不同头部的`ListView`,每次滑动到当前头部时就自动悬浮当前的头部?
diff --git "a/Android/Blog/AndroidBaseSkill/PullToRefreshListView\345\244\264\351\203\250\346\202\254\346\265\256\346\225\210\346\236\234\345\256\236\350\267\265.md" "b/Android/Blog/AndroidBaseSkill/PullToRefreshListView\345\244\264\351\203\250\346\202\254\346\265\256\346\225\210\346\236\234\345\256\236\350\267\265.md"
index 331f85a..05ff9e5 100644
--- "a/Android/Blog/AndroidBaseSkill/PullToRefreshListView\345\244\264\351\203\250\346\202\254\346\265\256\346\225\210\346\236\234\345\256\236\350\267\265.md"
+++ "b/Android/Blog/AndroidBaseSkill/PullToRefreshListView\345\244\264\351\203\250\346\202\254\346\265\256\346\225\210\346\236\234\345\256\236\350\267\265.md"
@@ -1 +1 @@
-#PullToRefeshListView使用
+# PullToRefeshListView使用
diff --git a/Android/Blog/AndroidBaseSkill/README.md b/Android/Blog/AndroidBaseSkill/README.md
index 4320384..ca0e60c 100644
--- a/Android/Blog/AndroidBaseSkill/README.md
+++ b/Android/Blog/AndroidBaseSkill/README.md
@@ -1 +1,5 @@
# Android 基本技能的使用
+* ListView悬浮头部实践
+* PullToRefreshListView头部悬浮效果实践
+* TabLayout+ViewPage_简单实现App底部Tab布局
+* Android屏幕适配及DisplayMetrics解析
diff --git "a/Android/Blog/AndroidBaseSkill/TabLayout+ViewPager_\347\256\200\345\215\225\345\256\236\347\216\260app\345\272\225\351\203\250Tab\345\270\203\345\261\200.md" "b/Android/Blog/AndroidBaseSkill/TabLayout+ViewPager_\347\256\200\345\215\225\345\256\236\347\216\260app\345\272\225\351\203\250Tab\345\270\203\345\261\200.md"
index 7b360dd..b558958 100644
--- "a/Android/Blog/AndroidBaseSkill/TabLayout+ViewPager_\347\256\200\345\215\225\345\256\236\347\216\260app\345\272\225\351\203\250Tab\345\270\203\345\261\200.md"
+++ "b/Android/Blog/AndroidBaseSkill/TabLayout+ViewPager_\347\256\200\345\215\225\345\256\236\347\216\260app\345\272\225\351\203\250Tab\345\270\203\345\261\200.md"
@@ -1,2 +1,170 @@
-[博客](http://www.jianshu.com/p/adf7a994613a)
-[博客](http://blog.csdn.net/m190607070/article/details/51852068)
+# TabLayout+ViewPager_简单实现app底部Tab布局
+[参考项目1](http://www.jianshu.com/p/adf7a994613a)
+[参考项目2](http://blog.csdn.net/m190607070/article/details/51852068)
+
+# 背景
+这个不是什么项目需求推动,只是自己在这方面使用的少,以前也不喜欢写博客,没有什么技术积累,自己参照网上的例子动手实践一下,加深印象。
+# 实践过程
+### 引入所需要的包
+因为`TabLayout`和`ViewPager`分别是属于`design`和`v4`包下的,所以我们先在`app`的`build.gradle`中添加:
+```
+compile 'com.android.support:design:25.1.0'
+compile 'com.android.support:support-v4:25.1.0'
+```
+### 编写布局文件
+```
+
+
+
+
+
+
+
+
+
+
+```
+
+`TabLayout`中`app:tabIndicatorHeight="0dp"`是为了不显示`tab`底部的横线,`app:tabMode="fixed"`是让底部`tab`布局不可滑动。
+
+### `Acitivity` 源代码:
+```
+package com.example.hankcoder.completequitapp;
+
+import android.os.Bundle;
+import android.support.design.widget.TabLayout;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentPagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.support.v7.app.AppCompatActivity;
+
+import com.example.hankcoder.completequitapp.Fragment.FragmentItem;
+
+public class Main3Activity extends AppCompatActivity {
+
+ private TabLayout mTlayout;
+ private ViewPager mViewPager;
+ private String[] mTitle;
+
+ private TabLayout.Tab mOne;
+ private TabLayout.Tab mTwo;
+ private TabLayout.Tab mThree;
+ private TabLayout.Tab mFour;
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.linearlayout_tab_vg);
+ initDatas();
+ initViews();
+ initEvents();
+ }
+
+ private void initDatas() {
+ mTitle = new String[]{"首页", "分类", "设置", "关于"};
+ }
+
+ private void initViews() {
+ mTlayout = (TabLayout) findViewById(R.id.tabLayout);
+ mViewPager = (ViewPager) findViewById(R.id.viewPager);
+
+ mViewPager.setAdapter(new FragmentPagerAdapter(getSupportFragmentManager()) {
+ @Override
+ public Fragment getItem(int position) {
+
+ if (position == 1) {
+ return new FragmentItem();
+ } else if (position == 2) {
+ return new FragmentItem();
+ } else if (position == 3) {
+ return new FragmentItem();
+ }
+ return new FragmentItem();
+ }
+
+ @Override
+ public int getCount() {
+ return mTitle.length;
+ }
+
+ @Override
+ public CharSequence getPageTitle(int position) {
+ return mTitle[position];
+ }
+ });
+
+ mTlayout.setupWithViewPager(mViewPager);
+ mOne = mTlayout.getTabAt(0);
+ mTwo = mTlayout.getTabAt(1);
+ mThree = mTlayout.getTabAt(2);
+ mFour = mTlayout.getTabAt(3);
+
+ mOne.setIcon(getResources().getDrawable(R.mipmap.ic_launcher_round));
+ mTwo.setIcon(getResources().getDrawable(R.mipmap.ic_launcher));
+ mThree.setIcon(getResources().getDrawable(R.mipmap.ic_launcher));
+ mFour.setIcon(getResources().getDrawable(R.mipmap.ic_launcher));
+ }
+
+ private void initEvents() {
+ mTlayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
+ @Override
+ public void onTabSelected(TabLayout.Tab tab) {
+ if (tab == mTlayout.getTabAt(0)) {
+ tab.setIcon(getResources().getDrawable(R.mipmap.ic_launcher_round));
+ mViewPager.setCurrentItem(0);
+ } else if (tab == mTlayout.getTabAt(1)) {
+ tab.setIcon(getResources().getDrawable(R.mipmap.ic_launcher_round));
+ mViewPager.setCurrentItem(1);
+ } else if (tab == mTlayout.getTabAt(2)) {
+ tab.setIcon(getResources().getDrawable(R.mipmap.ic_launcher_round));
+ mViewPager.setCurrentItem(2);
+ } else if (tab == mTlayout.getTabAt(3)) {
+ tab.setIcon(getResources().getDrawable(R.mipmap.ic_launcher_round));
+ mViewPager.setCurrentItem(3);
+ }
+ }
+
+ @Override
+ public void onTabUnselected(TabLayout.Tab tab) {
+ if (tab == mTlayout.getTabAt(0)) {
+ mOne.setIcon(getResources().getDrawable(R.mipmap.ic_launcher));
+ } else if (tab == mTlayout.getTabAt(1)) {
+ mTwo.setIcon(getResources().getDrawable(R.mipmap.ic_launcher));
+ } else if (tab == mTlayout.getTabAt(2)) {
+ mThree.setIcon(getResources().getDrawable(R.mipmap.ic_launcher));
+ }else if (tab == mTlayout.getTabAt(3)){
+ mFour.setIcon(getResources().getDrawable(R.mipmap.ic_launcher));
+ }
+ }
+
+ @Override
+ public void onTabReselected(TabLayout.Tab tab) {
+
+ }
+ });
+ }
+}
+```
+
+# 遗留问题
+直接左右滑动没问题,但是点击时,会有两个`tab`显示为红色?
diff --git a/Android/Blog/ThreeLibrary/README.md b/Android/Blog/ThreeLibrary/README.md
index 78c53f9..509a6e9 100644
--- a/Android/Blog/ThreeLibrary/README.md
+++ b/Android/Blog/ThreeLibrary/README.md
@@ -1,2 +1,2 @@
# Android 第三方库解析
-### 1. Android-Skin-Support
+* Android-Skin-Support
diff --git a/Android/Blog/ThreeLibrary/ReactNavite/README.md b/Android/Blog/ThreeLibrary/ReactNavite/README.md
new file mode 100644
index 0000000..3292695
--- /dev/null
+++ b/Android/Blog/ThreeLibrary/ReactNavite/README.md
@@ -0,0 +1,2 @@
+# React Native 项目(One 【一个】客户端)
+[参考项目](https://gold.xitu.io/post/58ba070b128fe10064394f68)
diff --git a/Android/Blog/ThreeLibrary/RecyclerViewBanner/README.md b/Android/Blog/ThreeLibrary/RecyclerViewBanner/README.md
new file mode 100644
index 0000000..f929463
--- /dev/null
+++ b/Android/Blog/ThreeLibrary/RecyclerViewBanner/README.md
@@ -0,0 +1,2 @@
+# 教你如何用 RecyclerView 做一个好用的轮播图
+[参考项目](https://gold.xitu.io/post/58b8b4fd8d6d8100652dc20f?utm_source=gold_browser_extension)
diff --git "a/Android/Code/Android\345\277\253\351\200\237\345\274\200\345\217\221\347\263\273\345\210\227 10\344\270\252\345\270\270\347\224\250\345\267\245\345\205\267\347\261\273.md" "b/Android/Code/Android\345\277\253\351\200\237\345\274\200\345\217\221\347\263\273\345\210\227 10\344\270\252\345\270\270\347\224\250\345\267\245\345\205\267\347\261\273.md"
new file mode 100644
index 0000000..49294a3
--- /dev/null
+++ "b/Android/Code/Android\345\277\253\351\200\237\345\274\200\345\217\221\347\263\273\345\210\227 10\344\270\252\345\270\270\347\224\250\345\267\245\345\205\267\347\261\273.md"
@@ -0,0 +1 @@
+# Android快速开发系列 10个常用工具类
diff --git a/Android/Code/README.md b/Android/Code/README.md
index 7d0a4ed..ec8de4d 100644
--- a/Android/Code/README.md
+++ b/Android/Code/README.md
@@ -1 +1,3 @@
-Android 模板代码
+# Android 模板代码
+* 获取未安装的APK图标、版本、包名、名称、是否安装、安装、打开
+* 从Assets拷贝文件到App外部缓存目录
diff --git "a/Android/Code/\344\273\216Assets\346\213\267\350\264\235\346\226\207\344\273\266\345\210\260App\345\244\226\351\203\250\347\274\223\345\255\230\347\233\256\345\275\225.md" "b/Android/Code/\344\273\216Assets\346\213\267\350\264\235\346\226\207\344\273\266\345\210\260App\345\244\226\351\203\250\347\274\223\345\255\230\347\233\256\345\275\225.md"
new file mode 100644
index 0000000..c7a21eb
--- /dev/null
+++ "b/Android/Code/\344\273\216Assets\346\213\267\350\264\235\346\226\207\344\273\266\345\210\260App\345\244\226\351\203\250\347\274\223\345\255\230\347\233\256\345\275\225.md"
@@ -0,0 +1,29 @@
+```
+private String copySkinFromAssets(String name) {
+ String skinDir = SkinFileUtils.getSkinDir(mAppContext);
+ String skinPath = skinDir + File.separator + name;
+ try {
+ InputStream is = mAppContext.getAssets().open(
+ SkinConstants.SKIN_DEPLOY_PATH
+ + File.separator
+ + name);
+ File fileDir = new File(skinDir);
+ if (!fileDir.exists()) {
+ fileDir.mkdirs();
+ }
+ OutputStream os = new FileOutputStream(skinPath);
+ int byteCount;
+ byte[] bytes = new byte[1024];
+
+ while ((byteCount = is.read(bytes)) != -1) {
+ os.write(bytes, 0, byteCount);
+ }
+ os.close();
+ is.close();
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return skinPath;
+}
+```
diff --git "a/Android/Code/\350\216\267\345\217\226\346\234\252\345\256\211\350\243\205\347\232\204APK\345\233\276\346\240\207\343\200\201\347\211\210\346\234\254\343\200\201\345\214\205\345\220\215\343\200\201\345\220\215\347\247\260\343\200\201\346\230\257\345\220\246\345\256\211\350\243\205\343\200\201\345\256\211\350\243\205\343\200\201\346\211\223\345\274\200.md" "b/Android/Code/\350\216\267\345\217\226\346\234\252\345\256\211\350\243\205\347\232\204APK\345\233\276\346\240\207\343\200\201\347\211\210\346\234\254\343\200\201\345\214\205\345\220\215\343\200\201\345\220\215\347\247\260\343\200\201\346\230\257\345\220\246\345\256\211\350\243\205\343\200\201\345\256\211\350\243\205\343\200\201\346\211\223\345\274\200.md"
new file mode 100644
index 0000000..1680f58
--- /dev/null
+++ "b/Android/Code/\350\216\267\345\217\226\346\234\252\345\256\211\350\243\205\347\232\204APK\345\233\276\346\240\207\343\200\201\347\211\210\346\234\254\343\200\201\345\214\205\345\220\215\343\200\201\345\220\215\347\247\260\343\200\201\346\230\257\345\220\246\345\256\211\350\243\205\343\200\201\345\256\211\350\243\205\343\200\201\346\211\223\345\274\200.md"
@@ -0,0 +1,71 @@
+# 获取未安装的APK图标、版本、包名、名称、是否安装、安装、打开
+## 获取APK图标
+```
+public static Drawable getApkIcon(Context context, String apkPath) {
+ PackageManager pm = context.getPackageManager();
+ PackageInfo info = pm.getPackageArchiveInfo(apkPath,
+ PackageManager.GET_ACTIVITIES);
+ if (info != null) {
+ ApplicationInfo appInfo = info.applicationInfo;
+ appInfo.sourceDir = apkPath;
+ appInfo.publicSourceDir = apkPath;
+ try {
+ return appInfo.loadIcon(pm);
+ } catch (OutOfMemoryError e) {
+ Log.e("ApkIconLoader", e.toString());
+ }
+ }
+ return null;
+ }
+```
+以下代码段中PackageManager、PackageInfo、ApplicationInfo均同上面一致。
+## 获取APK名称
+```
+String label = appInfo.loadLabel(mPackManager).toString();
+```
+## 获取APK包名
+```
+String packageName = appInfo.packageName;
+```
+## 获取APK版本
+```
+String version = info.versionName==null?"0":info.versionName
+```
+## 判断APK是否安装
+```
+private boolean isApkInstalled(String packagename)
+{
+ PackageManager localPackageManager = getPackageManager();
+ try
+ {
+ PackageInfo localPackageInfo = localPackageManager.getPackageInfo(packagename, PackageManager.GET_UNINSTALLED_PACKAGES);
+ return true;
+ } catch (PackageManager.NameNotFoundException localNameNotFoundException)
+ {
+ return false;
+ }
+}
+```
+## 安装APK
+```
+private void installAPK(String apkPath)
+{
+ Intent intent = new Intent();
+ intent.setAction(Intent.ACTION_VIEW);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.setDataAndType(Uri.parse("file://" + apkPath),
+ "application/vnd.android.package-archive");
+ mContext.startActivity(intent);
+}
+```
+## 打开APK
+```
+private void openAPK(String packagename)
+{
+ PackageManager packageManager = mContext.getPackageManager();
+ Intent intent=new Intent();
+ intent =packageManager.getLaunchIntentForPackage(packagename);
+ mContext.startActivity(intent);
+}
+```
+## 静默安装
diff --git "a/Java/Java\345\244\232\347\272\277\347\250\213\344\271\213\345\206\205\345\255\230\345\217\257\350\247\201\346\200\247.md" "b/Java/Java\345\244\232\347\272\277\347\250\213\344\271\213\345\206\205\345\255\230\345\217\257\350\247\201\346\200\247.md"
new file mode 100644
index 0000000..3b5e372
--- /dev/null
+++ "b/Java/Java\345\244\232\347\272\277\347\250\213\344\271\213\345\206\205\345\255\230\345\217\257\350\247\201\346\200\247.md"
@@ -0,0 +1,137 @@
+# Java多线程之内存可见性
+## 概念:
+#### 可见性:
+一个线程对共享变量值的修改,能够及时的被其它线程看到。
+#### 共享变量:
+如果一个变量在多个线程中都有工作副本,那么这个变量就是这几个线程的共享变量。
+#### Java内存模型(JMM)
+`Java`内存模型(`Java Memory Model`)描述了`Java`程序中各种变量(线程共享变量)的访问规则,以及在JVM中将变量存储到内存和从内存中读取出变量这样的底层细节。
+* 所有的变量都存储在主内存中
+* 每个线程都有自己独立的工作内存,里面保存该线程使用到的变量的副本(`主内存中该变量的一份拷贝`)
+* 线程对共享变量的所有操作都必须在自己的工作内存中进行,不能直接从主内存中读写
+* 不同线程之间无法直接访问其它线程工作内存中的变量,线程间变量值的传递需要通过主内存来完成
+
+#### 指令重排序
+代码书写的顺序与实际执行的顺序不同,指令重排序是编译器或处理器为了提高程序性能而做的优化
+* 编译器优化的重排序(编译器优化)
+* 指令级并行重排序(处理器优化)
+* 内存系统的重排序(处理器优化)
+
+`as-if-serial`: 无论如何重排序,程序执行的结果应该与代码顺序执行结果一致(`Java`编译器,运行时和处理器都会保证`Java`在单线程下遵循`as-if-serial`语义)
+`注:重排序不会给单线程带来内存可见性问题,多线程中程序交错执行时,重排序可能会造成内存可见性问题`
+
+## 共享变量可见性实现的原理
+线程`1`对共享变量的修改想要被线程`2`及时看到,必须要经过如下`2`个步骤:
+* 把工作内存`1`中更新过的共享变量刷新到主内存中
+* 将主内存中最新的共享变量的值更新到工作内存`2`中
+
+## 实现共享变量的可见性
+* 线程修改后的共享变量值能够及时从工作内存刷新到主内存中
+* 其它线程能及时把共享变量的最新值从主内存更新到自己的工作内存中
+
+## 导致共享变量在线程之间不可见的原因
+* 线程的交叉执行
+* 重排序结合线程交叉执行
+* 共享变量更新后的值没有在工作内存与主内存间及时更新
+
+## Java中实现可见性的方式
+* `synchronized`
+ * 原子性(同步)
+ * 可见性
+
+ ###### `JMM`关于`synchronized`的两条规定:
+ * 线程解锁前,必须把共享变量的最新赋值刷新大澳主内存中
+ * 线程加锁时,将清空工作内存中共享变量的值,从而使用共享变量时需要从主内存中重新读取最新的值(`注意:加锁与解锁需要是同一把锁`)
+线程解锁前对共享变量的修改在下次加锁时对其它线程可见
+
+ ###### 线程执行互斥代码的过程
+ * 获得互斥锁
+ * 清空工作区内存
+ * 从主内存拷贝变量的最新副本到工作内存
+ * 执行代码
+ * 将更改后的共享变量的值刷新到主内存
+ * 释放互斥锁
+
+ ###### 解决方案
+ * 线程锁提供了原子性,解决来线程的交叉执行,无论怎么重排序都是在一个锁块区域内和单线程执行没什么区别
+ * 可见性在线程解锁时会把共享变量的值刷新到主内存中
+* `volatile`
+ * 能够保证`volatile`变量的可见性
+ * 不能保证`volatile`变量复合操作的原子性
+
+ ###### `volatile` 如何实现内存可见性
+ 深入来说:通过加入内存屏障和禁止重排序优化来实现的
+ * 对`volatile`变量执行写操作时,会在写操作后加入一条`store`屏障指令
+ * 对`volatile`变量执行读操作时,会在读操作前加入一条`load`屏障指令
+
+ 通俗的说:`volatile`变量在每次被线程访问时,都强迫从主内存中重新读该变量的值,而当该变量发生变化时,又会强迫线程将最新的值刷新到主内存。这样任何时刻,不同的线程总能看到该变量的最新值。
+ ###### 线程写`volatile`变量的过程
+ * 改变线程工作内存中`volatile`变量副本的值
+ * 将改变后的副本的值从工作内存刷新到主内存
+
+ ###### 线程读`volatile`变量的过程
+ * 从主内存中读取`volatile`变量的最新值到线程的工作内存中
+ * 从工作内存中读取`volatile`变量的副本
+
+ ###### `volatile`不能保证`volatile`变量复合操作的原子性
+ ```
+ private int number = 0;
+ number ++; //不是原子操作
+ ```
+ * 读取number的值
+ * 将number的值加1
+ * 写入最新的number的值
+
+ ```
+ synchronized(this) {
+ number++;
+ }
+ ```
+ 上述代码能保证原子性,属于原子操作
+ ```
+ private volatile int number = 0;
+ ```
+ 上述代码添加`volatile`字段,也无法保证原子性
+
+ ###### `volatile`不能保证原子性分析
+ ```
+ number++ // 上面说了这步操作实际上分三步执行
+ ```
+ number = 5
+ 1.线程A读取number的值
+ 2.线程B读取number的值
+ 3.线程B执行加1操作
+ 4.线程B写入最新的number的值
+ 主内存:`number = 6`
+ 线程B工作内存:`number = 6`
+ 线程A工作内存:`number = 5`
+ 5.线程A执行加1操作
+ 6.线程A写入最新的`number`值
+ 结果:两个线程分别进行了加1,但是结果总共才增加1
+
+ ###### 保证`number`自增操作的原子性
+ * 使用`synchronized`关键字
+ 在自增操作的地方用,尽量缩小范围
+ * `JDK1.5`以后提供的`ReentrantLock`(`java.until.concurrent.locks`包下)
+ ```
+ private Lock lock = new ReentrantLock();
+ lock.lock();
+ try {
+ number++;
+ } finally {
+ lock.unlock();
+ }
+ ```
+ * 使用`AtomicInterger`(`vava.util.concurrent.atomic`包下)
+
+ ###### `volatile`使用场合
+ 要在多线程中安全使用`volatile`变量,必须同时满足:
+ 1.对变量的写入操作不依赖其当前值
+ * 不满足:`number++`, `count = count * 5`等
+ * 满足:boolean变量,纪录温度变化的变量等
+ 2.该变量没有包含在具有其它变量的不变式中
+
+## `synchronized`和`voaltile`比较
+* `volatile`不需要加锁,比`synchronized`更轻量级,不会阻塞线程;
+* 从内存可见性角度讲,`volatile`读相当于加锁,`volatile`写相当于解锁
+* `synchronized`既能保证可见性,又能保证原子性,而`volatile`只能保证可见性,无法保证原子性
diff --git a/Java/README.md b/Java/README.md
index bf4484b..3e1c4b1 100644
--- a/Java/README.md
+++ b/Java/README.md
@@ -1 +1,2 @@
-Java
+# Java
+* Java多线程之内存可见性
diff --git a/Life/Guitar/README.md b/Life/Guitar/README.md
new file mode 100644
index 0000000..f7660ee
--- /dev/null
+++ b/Life/Guitar/README.md
@@ -0,0 +1 @@
+# Guitar Study