开源项目阅读

仿网易云音乐

https://github.com/aa112901/remusic

RecyclerView多重itemType

    public class Adapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
        final static int FIRST_ITEM = 0;
        final static int ITEM = 1;

        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
            if (viewType == FIRST_ITEM)
                return new CommonItemViewHolder(LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.common_item
                , viewGroup, false));

            else {
                return new ListItemViewHolder(LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.fragment_musci_common_item
                , viewGroup, false));
            }
        }

        @Override
        public int getItemViewType(int position) {
            return position == FIRST_ITEM ? FIRST_ITEM : ITEM;
        }

        //将数据与界面进行绑定
        @Override
        public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
                model = mList.get(position - 1);

            if (holder instanceof ListItemViewHolder) {
                ((ListItemViewHolder) holder).mainTitle.setText(model.musicName.toString());
                ((ListItemViewHolder) holder).title.setText(model.artist.toString());
            } else if (holder instanceof CommonItemViewHolder) {
                ((CommonItemViewHolder) holder).textView.setText("(共" + mList.size() + "首)");
            }
        }

        //数据list不为空则返回size+1
        @Override
        public int getItemCount() {
            return (null != mList ? mList.size() + 1 : 0);
        }


        public class CommonItemViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
            TextView textView;
            ImageView select;
            CommonItemViewHolder(View view) {
                super(view);
                this.textView = (TextView) view.findViewById(R.id.play_all_number);
                this.select = (ImageView) view.findViewById(R.id.select);
            }
        }


        public class ListItemViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
            ImageView moreOverflow;
            TextView mainTitle, title;
            TintImageView playState;


            ListItemViewHolder(View view) {
                super(view);
                this.mainTitle = (TextView) view.findViewById(R.id.viewpager_list_toptext);
                this.title = (TextView) view.findViewById(R.id.viewpager_list_bottom_text);
                this.playState = (TintImageView) view.findViewById(R.id.play_state);
                this.moreOverflow = (ImageView) view.findViewById(R.id.viewpager_list_button);
            }
        }
    }

定时执行intent

        final Intent shutdownIntent = new Intent(this, MediaService.class);
        shutdownIntent.setAction(SHUTDOWN);

        mAlarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
        mShutdownIntent = PendingIntent.getService(this, 0, shutdownIntent, 0);
        mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
        //自启动以来过去的毫秒数+定时时长
        SystemClock.elapsedRealtime() + IDLE_DELAY, mShutdownIntent);       

电源管理

<uses-permission android:name="android.permission.WAKE_LOCK"/>
你可能还需要
<uses-permission android:name="android.permission.DEVICE_POWER"/>
        PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);  
        wakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK  
                | PowerManager.ON_AFTER_RELEASE, "DPA");
        //一般在resume中保持常量
        wakeLock.acquire();  
        //pause后释放
         wakeLock.release();  

具有过滤器的广播事件

        final IntentFilter filter = new IntentFilter();
        filter.addAction(SERVICECMD);
        filter.addAction(TOGGLEPAUSE_ACTION);
        filter.addAction(PAUSE_ACTION);
        filter.addAction(STOP_ACTION);
        filter.addAction(NEXT_ACTION);
        filter.addAction(PREVIOUS_ACTION);
        filter.addAction(PREVIOUS_FORCE_ACTION);
        filter.addAction(REPEAT_ACTION);
        filter.addAction(SHUFFLE_ACTION);
        filter.addAction(TRY_GET_TRACKINFO);
        filter.addAction(Intent.ACTION_SCREEN_OFF);
        filter.addAction(LOCK_SCREEN);
        filter.addAction(SEND_PROGRESS);
        filter.addAction(SETQUEUE);
        //注册广播事件,用于监听各种播放的操作事件
        registerReceiver(mIntentReceiver, filter);

监听外部存储的安装和卸载事件

        if (mUnmountReceiver == null) {
            mUnmountReceiver = new BroadcastReceiver() {
                @Override
                public void onReceive(final Context context, final Intent intent) {
                    final String action = intent.getAction();
                    if (action.equals(Intent.ACTION_MEDIA_EJECT)) {

                    } else if (action.equals(Intent.ACTION_MEDIA_MOUNTED)) {

                    }
                }
            };
            final IntentFilter filter = new IntentFilter();
            filter.addAction(Intent.ACTION_MEDIA_EJECT);
            filter.addAction(Intent.ACTION_MEDIA_MOUNTED);
            filter.addDataScheme("file");
            registerReceiver(mUnmountReceiver, filter);
        }

NotificationManager&AudioManager

mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
        mMediaButtonReceiverComponent = new ComponentName(getPackageName(),
                MediaButtonIntentReceiver.class.getName());
        mAudioManager.registerMediaButtonEventReceiver(mMediaButtonReceiverComponent);

播放音乐

mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
//注册不知道什么东西,回头再看
mMediaButtonReceiverComponent = new ComponentName(getPackageName(),
                MediaButtonIntentReceiver.class.getName());
mAudioManager.registerMediaButtonEventReceiver(mMediaButtonReceiverComponent);
        int status = mAudioManager.requestAudioFocus(mAudioFocusListener,
                AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
        //判断状态
        if (status != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
            return;
        }
        //给上面注册的广播发送东西
        final Intent intent = new Intent(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION);
        intent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, getAudioSessionId());
        intent.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, getPackageName());
        sendBroadcast(intent);
        //播放器
        mPlayer = new MultiPlayer(this);
        mPlayerHandler = new MusicPlayerHandler(this, mHandlerThread.getLooper());
        mPlayer.setHandler(mPlayerHandler);
        mPlayer.start();        

获取音乐数据

    //查询条件
    private static final String[] PROJECTION = new String[]{
            "audio._id AS _id", MediaStore.Audio.Media.ARTIST, MediaStore.Audio.Media.ALBUM,
            MediaStore.Audio.Media.TITLE, MediaStore.Audio.Media.DATA,
            MediaStore.Audio.Media.MIME_TYPE, MediaStore.Audio.Media.ALBUM_ID,
            MediaStore.Audio.Media.ARTIST_ID
    };

    //获取数据
            MatrixCursor cursor = new MatrixCursor(PROJECTION);
            cursor.addRow(new Object[]{info.songId, info.artist, info.albumName, info.musicName
                    , info.data, info.albumData, info.albumId, info.artistId});
            cursor.moveToFirst();
            mCursor = cursor;
            cursor.close();

    mCursor.getString(mCursor.getColumnIndexOrThrow(AudioColumns.DATA));
    mCursor.getString(mCursor.getColumnIndexOrThrow(AudioColumns.ALBUM));
    mCursor.getString(mCursor.getColumnIndexOrThrow(AudioColumns.MIME_TYPE));
    mCursor.getString(mCursor.getColumnIndexOrThrow(AudioColumns.TITLE));
    mCursor.getString(mCursor.getColumnIndexOrThrow(AudioColumns.ARTIST));
    mCursor.getLong(mCursor.getColumnIndexOrThrow(AudioColumns.ALBUM_ID));
    mCursor.getLong(mCursor.getColumnIndexOrThrow(AudioColumns.ARTIST_ID));        

子线程的Handler

    private Thread mLrcThread = new Thread(new Runnable() {
        @Override
        public void run() {
            Looper.prepare();
            mLrcHandler = new Handler();
            Looper.loop();
        }
    });
    mLrcThread .start();
//也可以直接用HandlerThread
        mHandlerThread = new HandlerThread("MusicPlayerHandler",
                android.os.Process.THREAD_PRIORITY_BACKGROUND);
        mHandlerThread.start();
mPlayerHandler = new MusicPlayerHandler(this, mHandlerThread.getLooper());        

ObjectAnimator的反向动画

anim.start();
anim.reverse();

设置Window的背景图

mContext.getWindow().setBackgroundDrawableResource(R.color.background_material_light_1);

RecyclerViewItem动画

((SimpleItemAnimator) recyclerView.getItemAnimator()).setSupportsChangeAnimations(false);

动态权限处理

        if (CommonUtils.isLollipop() && ContextCompat.checkSelfPermission(mContext, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions((Activity) mContext,new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 0);
        }
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            reloadAdapter();
        }
    }

继承实现,ViewPager触摸事件处理

    @Override
    public boolean onTouchEvent(MotionEvent evt) {
        switch (evt.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // 记录按下时候的坐标
                downPoint.x = evt.getX();
                downPoint.y = evt.getY();
                if (this.getChildCount() > 1) {
                    //有内容,多于1个时
                    // 通知其父控件,现在进行的是本控件的操作,不允许拦截
                    getParent().requestDisallowInterceptTouchEvent(true);
                }
                break;
            case MotionEvent.ACTION_MOVE:
                if (this.getChildCount() > 1) { //有内容,多于1个时
                    // 通知其父控件,现在进行的是本控件的操作,不允许拦截
                    getParent().requestDisallowInterceptTouchEvent(true);
                }
                break;
            case MotionEvent.ACTION_UP:
                // 在up时判断是否按下和松手的坐标为一个点
                if (PointF.length(evt.getX() - downPoint.x, evt.getY()
                        - downPoint.y) < (float) 5.0) {
                    onSingleTouch(this);
                    return true;
                }
                break;
        }
        return super.onTouchEvent(evt);
    }

输入法管理器的使用

InputMethodManager mImm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
mImm.hideSoftInputFromWindow(mSearchView.getWindowToken(), 0);

SearchView的使用

//android.support.v7.widget.SearchView
        //设置搜索监听器
        mSearchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            //提交监听,为真表示已经处理,false表示使用默认动作
            public boolean onQueryTextSubmit(String query) {
                return false;
            }

            @Override
            public boolean onQueryTextChange(String newText) {
                return false;
            }
        });
        mSearchView.setQueryHint(getResources().getString(R.string.search_net_music));
        //关闭默认图标化
        mSearchView.setIconifiedByDefault(false);
        //关闭图标化
        mSearchView.setIconified(false);

Toolbar的使用

    <com.bilibili.magicasakura.widgets.TintToolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/theme_color_primary"
        android:minHeight="?attr/actionBarSize"
        android:theme="@style/Theme.AppCompat"
        app:popupTheme="@style/ThemeOverlay.AppCompat.Dark" >
        <layout..
            android:layout_height="?actionBarSize"
            >
        </>
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        toolbar.setPadding(0, CommonUtils.getStatusHeight(this), 0, 0);
        //需要设置为noActionBar
        setSupportActionBar(toolbar);
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);

        ActionBar ab = getSupportActionBar();
        //home图标
        ab.setHomeAsUpIndicator(R.drawable.ic_menu);
        ab.setTitle("");
<?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/menu_search"
        android:orderInCategory="1"
        android:title="@string/search"
        app:actionViewClass="android.support.v7.widget.SearchView"
        app:showAsAction="ifRoom|collapseActionView" />
</menu>
    @Override
    public boolean onCreateOptionsMenu(final Menu menu) {
        getMenuInflater().inflate(R.menu.menu_search, menu);
        mSearchView = (SearchView) MenuItemCompat.getActionView(menu.findItem(R.id.menu_search));

        MenuItemCompat.setOnActionExpandListener(menu.findItem(R.id.menu_search), 
                new MenuItemCompat.OnActionExpandListener() {
            @Override
            public boolean onMenuItemActionExpand(MenuItem item) {
                return true;
            }

            @Override
            public boolean onMenuItemActionCollapse(MenuItem item) {
                return false;
            }
        });
        menu.findItem(R.id.menu_search).expandActionView();
        return super.onCreateOptionsMenu(menu);
    }

这里写图片描述

    @Override
    public boolean onOptionsItemSelected(final MenuItem item) {
        switch (item.getItemId()) {
            //toolbar左侧home图标
            case android.R.id.home:
                finish();
                return true;
            default:
                break;
        }
        return super.onOptionsItemSelected(item);
    }

舒服的AsyncTask使用方法

        new AsyncTask<Boolean, Void, Boolean>() {
            @Override
            protected Boolean doInBackground(Boolean... params) {
                SystemClock.sleep(3000);
                return true;
            }

            @Override
            protected void onPostExecute(Boolean load) {
                super.onPostExecute(load);
                }
            }
        }.execute();

自定义边界的位图

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid android:color="@color/white_text" />
    <stroke
        android:color="@color/test_color"
        android:width="1px">
    </stroke>
</shape>

帧动画 实现进度条效果

//res/drawable/list_loading.xml
<animation-list android:oneshot="false"
  xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:duration="133" android:drawable="@drawable/loading1" />
    <item android:duration="133" android:drawable="@drawable/loading2" />
    <item android:duration="133" android:drawable="@drawable/loading3" />
    <item android:duration="133" android:drawable="@drawable/loading4" />
    <item android:duration="133" android:drawable="@drawable/loading3" />
    <item android:duration="133" android:drawable="@drawable/loading2" />
</animation-list>
        AnimationDrawable animationDrawable = (AnimationDrawable) getResources().getDrawable(
                R.drawable.list_loading);
        barmusic.setBackground(animationDrawable);
        animationDrawable.start();

以Dialog做引导图

public class SplashScreen {
    public final static int SLIDE_LEFT = 1;
    public final static int SLIDE_UP = 2;
    public final static int FADE_OUT = 3;

    private Dialog splashDialog;
    private Activity activity;
    public SplashScreen(Activity activity) {
        this.activity = activity;
    }

    /**
     * @param animation     消失时的动画效果,取值可以是:SplashScreen.SLIDE_LEFT, SplashScreen.SLIDE_UP, SplashScreen.FADE
     */
    public void show(final int imageResource, final int animation) {
        Runnable runnable = new Runnable() {
            public void run() {
                //获取屏幕的显示参数
                DisplayMetrics metrics = new DisplayMetrics();
                //设置跟布局的参数
                LinearLayout root = new LinearLayout(activity);
                root.setMinimumHeight(metrics.heightPixels);
                root.setMinimumWidth(metrics.widthPixels);
                root.setOrientation(LinearLayout.VERTICAL);
                root.setBackgroundColor(Color.BLACK);
                root.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                        ViewGroup.LayoutParams.MATCH_PARENT, 0.0F));
                root.setBackgroundResource(imageResource);
                //以透明主题创建Dialog
                splashDialog = new Dialog(activity, android.R.style.Theme_Translucent_NoTitleBar);
                //设置为全屏展示
                if ((activity.getWindow().getAttributes().flags & WindowManager.LayoutParams.FLAG_FULLSCREEN)
                        == WindowManager.LayoutParams.FLAG_FULLSCREEN) {
                    splashDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                            WindowManager.LayoutParams.FLAG_FULLSCREEN);
                }
                //设置window的消失方式
                Window window = splashDialog.getWindow();
                switch (animation) {
                    case SLIDE_LEFT:
                        window.setWindowAnimations(R.style.dialog_anim_slide_left);
                        break;
                    case SLIDE_UP:
                        window.setWindowAnimations(R.style.dialog_anim_slide_up);
                        break;
                    case FADE_OUT:
                        window.setWindowAnimations(R.style.dialog_anim_fade_out);
                        break;
                }

                splashDialog.setContentView(root);
                splashDialog.setCancelable(false);
                splashDialog.show();
            }
        };
        activity.runOnUiThread(runnable);
    }

    public void removeSplashScreen() {
        if (splashDialog != null && splashDialog.isShowing()) {
            splashDialog.dismiss();
            splashDialog = null;
        }
    }
}
//res/values/styles.xml
<resources xmlns:android="http://schemas.android.com/tools">
   <style name="dialog_anim_slide_left" mce_bogus="1" parent="android:Animation">
        <item name="android:windowExitAnimation">@anim/dialog_exit_slide_left</item>
    </style>

    <style name="dialog_anim_slide_up" mce_bogus="1" parent="android:Animation">
        <item name="android:windowExitAnimation">@anim/dialog_exit_slide_up</item>
    </style>

    <style name="dialog_anim_fade_out" mce_bogus="1" parent="android:Animation">
        <item name="android:windowExitAnimation">@anim/dialog_exit_fade_out</item>
    </style>
</resources>    
//res/anim/dialog_exit_slide_left.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:duration="3000"
        android:fromXDelta="0%"
        android:fromYDelta="0%"
        android:interpolator="@android:anim/accelerate_interpolator"
        android:toXDelta="-100%"
        android:toYDelta="0%" />
</set>
//res/anim/dialog_exit_fade_out.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <alpha
        android:duration="3000"
        android:fromAlpha="1.0"
        android:interpolator="@android:anim/accelerate_interpolator"
        android:toAlpha="0.0" />
</set>
//res/anim/dialog_exit_slide_up.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:duration="500"
        android:fromXDelta="0%"
        android:fromYDelta="0%"
        android:interpolator="@android:anim/accelerate_interpolator"
        android:toXDelta="0%"
        android:toYDelta="-100%" />
</set>

返回桌面

                Intent intent = new Intent(Intent.ACTION_MAIN);
                intent.addCategory(Intent.CATEGORY_HOME);
                startActivity(intent);
    //双击返回桌面
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            if ((System.currentTimeMillis() - time > 1000)) {
                Toast.makeText(this, "再按一次返回桌面", Toast.LENGTH_SHORT).show();
                time = System.currentTimeMillis();
            } else {
                Intent intent = new Intent(Intent.ACTION_MAIN);
                intent.addCategory(Intent.CATEGORY_HOME);
                startActivity(intent);
            }
            return true;
        } else {
            return super.onKeyDown(keyCode, event);
        }
    }

某音乐播放器

Timber
https://github.com/naman14/Timber
运用了谷歌的播放框架,看不懂,换一个

静态Fragment的使用

        FrameLayout contentRoot = findViewById(R.id.content_root);
        contentRoot.addView(LayoutInflater.from(this)
                    .inflate(R.layout.fragment_cast_mini_controller, null), params);
        findViewById(R.id.castMiniController).setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    startActivity(new Intent(MainActivity.this, ExpandedControllerActivity.class));
                }
            });
//res/layout/fragment_cast_mini_controller.xml
<?xml version="1.0" encoding="utf-8"?>
<fragment
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/castMiniController"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:layout_gravity="bottom"
    android:gravity="bottom"
    android:visibility="gone"
    class="com.google.android.gms.cast.framework.media.widget.MiniControllerFragment"/>

动态权限的处理

//权限处理的封装
public class Nammu {
    private static final String TAG = Nammu.class.getSimpleName();
    private static final String KEY_PREV_PERMISSIONS = "previous_permissions";
    private static final String KEY_IGNORED_PERMISSIONS = "ignored_permissions";
    private static Context context;
    private static SharedPreferences sharedPreferences;
    private static ArrayList<PermissionRequest> permissionRequests = new ArrayList<PermissionRequest>();

    public static void init(Context context) {
        sharedPreferences = context.getSharedPreferences("pl.tajchert.runtimepermissionhelper", Context.MODE_PRIVATE);
        Nammu.context = context;
    }

    /**
     * Check that all given permissions have been granted by verifying that each entry in the
     * given array is of the value {@link PackageManager#PERMISSION_GRANTED}.
     */
    public static boolean verifyPermissions(int[] grantResults) {
        for (int result : grantResults) {
            if (result != PackageManager.PERMISSION_GRANTED) {
                return false;
            }
        }
        return true;
    }

    /**
     * Returns true if the Activity has access to given permissions.
     */
    public static boolean hasPermission(Activity activity, String permission) {
        return activity.checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED;
    }

    /**
     * Returns true if the Activity has access to a all given permission.
     */
    public static boolean hasPermission(Activity activity, String[] permissions) {
        for (String permission : permissions) {
            if (activity.checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
                return false;
            }
        }
        return true;
    }

    /*
     * If we override other methods, lets do it as well, and keep name same as it is already weird enough.
     * Returns true if we should show explanation why we need this permission.
     */
    public static boolean shouldShowRequestPermissionRationale(Activity activity, String permissions) {
        return activity.shouldShowRequestPermissionRationale(permissions);
    }

    public static void askForPermission(Activity activity, String permission, PermissionCallback permissionCallback) {
        askForPermission(activity, new String[]{permission}, permissionCallback);
    }

    public static void askForPermission(Activity activity, String[] permissions, PermissionCallback permissionCallback) {
        if (permissionCallback == null) {
            return;
        }
        if (hasPermission(activity, permissions)) {
            permissionCallback.permissionGranted();
            return;
        }
        PermissionRequest permissionRequest = new PermissionRequest(new ArrayList<String>(Arrays.asList(permissions)), permissionCallback);
        permissionRequests.add(permissionRequest);

        activity.requestPermissions(permissions, permissionRequest.getRequestCode());
    }

    public static void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        PermissionRequest requestResult = new PermissionRequest(requestCode);
        if (permissionRequests.contains(requestResult)) {
            PermissionRequest permissionRequest = permissionRequests.get(permissionRequests.indexOf(requestResult));
            if (verifyPermissions(grantResults)) {
                //Permission has been granted
                permissionRequest.getPermissionCallback().permissionGranted();
            } else {
                permissionRequest.getPermissionCallback().permissionRefused();

            }
            permissionRequests.remove(requestResult);
        }
        refreshMonitoredList();
    }


    //Permission monitoring part below

    /**
     * Get list of currently granted permissions, without saving it inside Nammu
     *
     * @return currently granted permissions
     */
    public static ArrayList<String> getGrantedPermissions() {
        if (context == null) {
            throw new RuntimeException("Must call init() earlier");
        }
        ArrayList<String> permissions = new ArrayList<String>();
        ArrayList<String> permissionsGranted = new ArrayList<String>();
        //Group location
        permissions.add(Manifest.permission.ACCESS_FINE_LOCATION);
        permissions.add(Manifest.permission.ACCESS_COARSE_LOCATION);
        //Group Calendar
        permissions.add(Manifest.permission.WRITE_CALENDAR);
        permissions.add(Manifest.permission.READ_CALENDAR);
        //Group Camera
        permissions.add(Manifest.permission.CAMERA);
        //Group Contacts
        permissions.add(Manifest.permission.WRITE_CONTACTS);
        permissions.add(Manifest.permission.READ_CONTACTS);
        permissions.add(Manifest.permission.GET_ACCOUNTS);
        //Group Microphone
        permissions.add(Manifest.permission.RECORD_AUDIO);
        //Group Phone
        permissions.add(Manifest.permission.CALL_PHONE);
        permissions.add(Manifest.permission.READ_PHONE_STATE);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            permissions.add(Manifest.permission.READ_CALL_LOG);
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            permissions.add(Manifest.permission.WRITE_CALL_LOG);
        }
        permissions.add(Manifest.permission.ADD_VOICEMAIL);
        permissions.add(Manifest.permission.USE_SIP);
        permissions.add(Manifest.permission.PROCESS_OUTGOING_CALLS);
        //Group Body sensors
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
            permissions.add(Manifest.permission.BODY_SENSORS);
        }
        //Group SMS
        permissions.add(Manifest.permission.SEND_SMS);
        permissions.add(Manifest.permission.READ_SMS);
        permissions.add(Manifest.permission.RECEIVE_SMS);
        permissions.add(Manifest.permission.RECEIVE_WAP_PUSH);
        permissions.add(Manifest.permission.RECEIVE_MMS);
        //Group Storage
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            permissions.add(Manifest.permission.READ_EXTERNAL_STORAGE);
        }
        permissions.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
        for (String permission : permissions) {
            if (context.checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED) {
                permissionsGranted.add(permission);
            }
        }
        return permissionsGranted;
    }

    /**
     * Refresh currently granted permission list, and save it for later comparing using @permissionCompare()
     */
    public static void refreshMonitoredList() {
        ArrayList<String> permissions = getGrantedPermissions();
        Set<String> set = new HashSet<String>();
        for (String perm : permissions) {
            set.add(perm);
        }
        sharedPreferences.edit().putStringSet(KEY_PREV_PERMISSIONS, set).apply();
    }

    /**
     * Get list of previous Permissions, from last refreshMonitoredList() call and they may be outdated,
     * use getGrantedPermissions() to get current
     */
    public static ArrayList<String> getPreviousPermissions() {
        ArrayList<String> prevPermissions = new ArrayList<String>();
        prevPermissions.addAll(sharedPreferences.getStringSet(KEY_PREV_PERMISSIONS, new HashSet<String>()));
        return prevPermissions;
    }

    public static ArrayList<String> getIgnoredPermissions() {
        ArrayList<String> ignoredPermissions = new ArrayList<String>();
        ignoredPermissions.addAll(sharedPreferences.getStringSet(KEY_IGNORED_PERMISSIONS, new HashSet<String>()));
        return ignoredPermissions;
    }

    /**
     * Lets see if we already ignore this permission
     */
    public static boolean isIgnoredPermission(String permission) {
        if (permission == null) {
            return false;
        }
        return getIgnoredPermissions().contains(permission);
    }

    /**
     * Use to ignore to particular Permission - even if user will deny or add it we won't receive a callback.
     *
     * @param permission Permission to ignore
     */
    public static void ignorePermission(String permission) {
        if (!isIgnoredPermission(permission)) {
            ArrayList<String> ignoredPermissions = getIgnoredPermissions();
            ignoredPermissions.add(permission);
            Set<String> set = new HashSet<String>();
            set.addAll(ignoredPermissions);
            sharedPreferences.edit().putStringSet(KEY_IGNORED_PERMISSIONS, set).apply();
        }
    }

    /**
     * Used to trigger comparing process - @permissionListener will be called each time Permission was revoked, or added (but only once).
     *
     * @param permissionListener Callback that handles all permission changes
     */
    public static void permissionCompare(PermissionListener permissionListener) {
        if (context == null) {
            throw new RuntimeException("Before comparing permissions you need to call Nammu.init(context)");

        }
        ArrayList<String> previouslyGranted = getPreviousPermissions();
        ArrayList<String> currentPermissions = getGrantedPermissions();
        ArrayList<String> ignoredPermissions = getIgnoredPermissions();
        for (String permission : ignoredPermissions) {
            if (previouslyGranted != null && !previouslyGranted.isEmpty()) {
                if (previouslyGranted.contains(permission)) {
                    previouslyGranted.remove(permission);
                }
            }

            if (currentPermissions != null && !currentPermissions.isEmpty()) {
                if (currentPermissions.contains(permission)) {
                    currentPermissions.remove(permission);
                }
            }
        }
        for (String permission : currentPermissions) {
            if (previouslyGranted.contains(permission)) {
                //All is fine, was granted and still is
                previouslyGranted.remove(permission);
            } else {
                //We didn't have it last time
                if (permissionListener != null) {
                    permissionListener.permissionsChanged(permission);
                    permissionListener.permissionsGranted(permission);
                }
            }
        }
        if (previouslyGranted != null && !previouslyGranted.isEmpty()) {
            //Something was granted and removed
            for (String permission : previouslyGranted) {
                if (permissionListener != null) {
                    permissionListener.permissionsChanged(permission);
                    permissionListener.permissionsRemoved(permission);
                }
            }
        }
        refreshMonitoredList();
    }

    /**
     * Not that needed method but if we override others it is good to keep same.
     */
    public static boolean checkPermission(String permissionName) {
        if (context == null) {
            throw new RuntimeException("Before comparing permissions you need to call Nammu.init(context)");
        }
        return PackageManager.PERMISSION_GRANTED == context.checkSelfPermission(permissionName);
    }
}
//使用前检查权限
        if (Nammu.checkPermission(Manifest.permission.READ_EXTERNAL_STORAGE) && Nammu.checkPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
            loadEverything();
        } else {
            if (Nammu.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_EXTERNAL_STORAGE)) {
                Snackbar.make(panelLayout, "Timber will need to read external storage to display songs on your device.",
                        Snackbar.LENGTH_INDEFINITE)
                        .setAction("OK", new View.OnClickListener() {
                            @Override
                            public void onClick(View view) {
                                Nammu.askForPermission(MainActivity.this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}
                                , permissionReadstorageCallback);
                            }
                        }).show();
            } else {
                Nammu.askForPermission(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}
                , permissionReadstorageCallback);
            }
        }
//检查权限申请结果
    @Override
    public void onRequestPermissionsResult(
            int requestCode, String[] permissions, int[] grantResults) {
        Nammu.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }       

DrawerLayout

<?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"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">//自动填充

    <include layout="@layout/include_list_viewpager" />

    <android.support.design.widget.NavigationView//navigationView的使用
        android:id="@+id/nav_view"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"//设置方位
        android:fitsSystemWindows="true"
        app:menu="@menu/drawer_view" />
</android.support.v4.widget.DrawerLayout>
//res/menu/drawer_view.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <group
        android:id="@+id/group1"
        android:checkableBehavior="single">
        <item
            android:id="@+id/nav_library"
            android:title="@string/library" />
        <item
            android:id="@+id/nav_playlists"
            android:title="@string/playlists" />
        <item
            android:id="@+id/nav_folders"
            android:title="@string/folders" />
        <item
            android:id="@+id/nav_queue"
            android:title="@string/playing_queue" />
        <item
            android:id="@+id/nav_nowplaying"
            android:checkable="false"
            android:title="@string/now_playing" />
    </group>

    <group
        android:id="@+id/group2"
        android:checkableBehavior="none">
        <item
            android:id="@+id/nav_settings"
            android:title="@string/settings" />
        <item
            android:id="@+id/nav_about"
            android:title="@string/about" />
        <item
            android:id="@+id/nav_donate"
            android:title="Support development" />
    </group>
</menu>
//res/layout/nav_header
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="208dp"
    android:background="?attr/colorPrimaryDark"
    android:theme="@style/ThemeOverlay.AppCompat.Dark"
    android:orientation="vertical"
    android:gravity="bottom">

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="centerCrop"
        android:id="@+id/album_art"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_gravity="bottom"
        android:gravity="bottom">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/song_title"
        android:textSize="18sp"
        android:textColor="#ffffff"
        android:singleLine="true"
        android:ellipsize="end"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="10dp"
        android:textAppearance="@style/TextAppearance.AppCompat.Body1"/>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/song_artist"
        android:paddingTop="2dp"
        android:textSize="15sp"
        android:textColor="#ffffff"
        android:singleLine="true"
        android:ellipsize="end"
        android:layout_marginBottom="10dp"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="10dp"
        android:textAppearance="@style/TextAppearance.AppCompat.Body1"/>
    </LinearLayout>

    <View
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="?attr/selectableItemBackground"/>
</FrameLayout>
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
//navigationView的使用
navigationView = (NavigationView) findViewById(R.id.nav_view);
View header = navigationView.inflateHeaderView(R.layout.nav_header);

navigationView.setNavigationItemSelectedListener(
        new NavigationView.OnNavigationItemSelectedListener() {
            @Override
            public boolean onNavigationItemSelected(final MenuItem menuItem) {
                       switch (menuItem.getItemId()) {
                        case R.id.nav_library:
                            runnable = navigateLibrary;
                            break;
                        case R.id.nav_playlists:
                            runnable = navigatePlaylist;
                            break;
                        case R.id.nav_folders:
                            runnable = navigateFolder;
                            break;
                        case R.id.nav_nowplaying:
                            if (getCastSession() != null) {
                                startActivity(new Intent(MainActivity.this, ExpandedControlsActivity.class));
                            } else {
                                NavigationUtils.navigateToNowplaying(MainActivity.this, false);
                            }
                            break;
                        case R.id.nav_donate:
                            startActivity(new Intent(MainActivity.this, DonateActivity.class));
                            break;
                    }
                return true;
            }
        });

            navigationView.getMenu().findItem(R.id.nav_library).setIcon(R.drawable.library_music);
            navigationView.getMenu().findItem(R.id.nav_playlists).setIcon(R.drawable.playlist_play);
            navigationView.getMenu().findItem(R.id.nav_settings).setIcon(R.drawable.settings);
            navigationView.getMenu().findItem(R.id.nav_about).setIcon(R.drawable.information);
            navigationView.getMenu().findItem(R.id.nav_donate).setIcon(R.drawable.payment_black);
//需要设置打开menu
   @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        super.onCreateOptionsMenu(menu);
        return true;
    }
//菜单选中的事件监听
   @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case android.R.id.home: {
                if (isNavigatingMain()) {
                    mDrawerLayout.openDrawer(GravityCompat.START);
                } else super.onBackPressed();
                return true;
            }
        }
        return super.onOptionsItemSelected(item);
    }
//返回按键的处理
    @Override
    public void onBackPressed() {
        if (mDrawerLayout.isDrawerOpen(GravityCompat.START)) {
            mDrawerLayout.closeDrawer(GravityCompat.START);
        } else {
            super.onBackPressed();
        }
    }

北京新闻

卡死了....
Gallery的使用
<Gallery
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/gallery"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="center_vertical"
    android:spacing="1dip" />
    //和listview使用方法一样
    private static class ImageAdapter extends BaseAdapter {
        private LayoutInflater inflater;
        private DisplayImageOptions options;
        ImageAdapter(Context context) {
            inflater = LayoutInflater.from(context);
        }
        @Override
        public int getCount() {
            return IMAGE_URLS.length;
        }
        @Override
        public Object getItem(int position) {
            return position;
        }
        @Override
        public long getItemId(int position) {
            return position;
        }
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ImageView imageView = (ImageView) convertView;
            if (imageView == null) {
                imageView = (ImageView) inflater.inflate(R.layout.item_gallery_image, parent, false);
            }
            ImageLoader.getInstance().displayImage(IMAGE_URLS[position], imageView, options);
            return imageView;
        }
    }
SparseArray
    /**
     * SparseArray替换HashMap,性能好于HashMap
     */
     //原因是避免了在自动装箱时创建无用的对象
    private SparseArray<T> datas;
    datas.put(int , t);
解决PhotoView因延迟加载,测量错误导致的bug
        PhotoView photoView = (PhotoView) findViewById(R.id.iv_photo);
        final PhotoViewAttacher attacher = new PhotoViewAttacher(photoView);
        Picasso.with(this)
                .load(url)
                .into(photoView, new Callback() {
                    @Override
                    public void onSuccess() {
                        //加载完成后重新测量大小
                        attacher.update();
                    }

                    @Override
                    public void onError() {
                    }
                });
自定义不可滚动的viewpager
public class NoScrollViewPager extends ViewPager {
    /**
     * 通常在代码中实例化的时候用该方法
     * @param context
     */
    public NoScrollViewPager(Context context) {
        super(context);
    }

    /**
     * 在布局文件中使用该类的时候,实例化该类用该构造方法,这个方法不能少,少的化会崩溃。
     * @param context
     * @param attrs
     */
    public NoScrollViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    /**
     * 重写触摸事件,消费掉
     * @param ev
     * @return
     */
    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        return true;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return false;
    }
}
RadioGroup的使用
    <RadioGroup
        android:id="@+id/rg_main"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/bottom_tab_bg"
        android:orientation="horizontal">
        <RadioButton
            android:id="@+id/rb_home"
            android:drawableTop="@drawable/rb_home_drawable_selector"
            android:gravity="center"
            android:text="主页"
            style="@style/bottom_tag_style"
            />
        <RadioButton
            android:id="@+id/rb_newscenter"
            android:drawableTop="@drawable/rb_newscenter_drawable_selector"
            android:gravity="center"
            android:text="新闻"
            style="@style/bottom_tag_style"
            />
        <RadioButton
            android:id="@+id/rb_smartservice"
            android:drawableTop="@drawable/rb_smartservice_drawable_selector"
            android:gravity="center"
            android:text="商城"
            style="@style/bottom_tag_style"
            />
        <RadioButton
            android:id="@+id/rb_govaffair"
            android:drawableTop="@drawable/rb_govaffair_drawable_selector"
            android:gravity="center"
            android:text="购物车"
            style="@style/bottom_tag_style"
            />
        <RadioButton
            android:id="@+id/rb_setting"
            android:drawableTop="@drawable/rb_setting_drawable_selector"
            android:gravity="center"
            android:text="设置"
            style="@style/bottom_tag_style"
            />
    </RadioGroup>
//默认选中
rg_main.check(R.id.rb_home);
//设置RadioGroup的选中状态改变的监听
    rg_main.setOnCheckedChangeListener(new MyOnCheckedChangeListener());
    class MyOnCheckedChangeListener implements RadioGroup.OnCheckedChangeListener {
        /**
         * @param group RadioGroup
         * @param checkedId 被选中的RadioButton的id
         */
        @Override
        public void onCheckedChanged(RadioGroup group, int checkedId) {
            switch (checkedId){
                case R.id.rb_home://主页radioButton的id
                    viewpager.setCurrentItem(0,false);
                    isEnableSlidingMenu(SlidingMenu.TOUCHMODE_NONE);
                    break;
                case R.id.rb_newscenter://新闻中心radioButton的id
                    viewpager.setCurrentItem(1,false);
                    isEnableSlidingMenu(SlidingMenu.TOUCHMODE_FULLSCREEN);
                    break;
                case R.id.rb_smartservice://智慧服务radioButton的id
                    viewpager.setCurrentItem(2,false);
                    isEnableSlidingMenu(SlidingMenu.TOUCHMODE_NONE);
                    break;
                case R.id.rb_govaffair://政要指南的RadioButton的id
                    viewpager.setCurrentItem(3,false);
                    isEnableSlidingMenu(SlidingMenu.TOUCHMODE_NONE);
                    break;
                case R.id.rb_setting://设置中心RadioButton的id
                    viewpager.setCurrentItem(4,false);
                    isEnableSlidingMenu(SlidingMenu.TOUCHMODE_NONE);
                    break;
            }
        }
    }
//配合Fragment使用的逻辑
        switch (frIndex) {
            case ImageListFragment.INDEX:
                tag = ImageListFragment.class.getSimpleName();
                fr = getSupportFragmentManager().findFragmentByTag(tag);
                if (fr == null) {
                    fr = new ImageListFragment();
                }
                titleRes = R.string.ac_name_image_list;
                break;
            case ImageGridFragment.INDEX:
            case ImagePagerFragment.INDEX:
            case ImageGalleryFragment.INDEX:
        }
        getSupportFragmentManager().beginTransaction().replace(android.R.id.content, fr, tag).commit();
自定义崩溃收集
public class CrashHandler implements UncaughtExceptionHandler {

    public static final String TAG = "CrashHandler";

    //系统默认的UncaughtException处理类
    private Thread.UncaughtExceptionHandler mDefaultHandler;
    //CrashHandler实例
    private static CrashHandler instance;
    //程序的Context对象
    private Context mContext;
    //用来存储设备信息和异常信息
    private Map<String, String> infos = new HashMap<String, String>();

    //用于格式化日期,作为日志文件名的一部分
    private DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");

    /** 保证只有一个CrashHandler实例 */
    private CrashHandler() {}

    /** 获取CrashHandler实例 ,单例模式 */
    public static CrashHandler getInstance() {
        if(instance == null)
            instance = new CrashHandler();
        return instance;
    }

    /**
     * 初始化
     */
    public void init(Context context) {
        mContext = context;
        //获取系统默认的UncaughtException处理器
        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
        //设置该CrashHandler为程序的默认处理器
        Thread.setDefaultUncaughtExceptionHandler(this);
    }

    /**
     * 当UncaughtException发生时会转入该函数来处理
     */
    @Override
    public void uncaughtException(Thread thread, Throwable ex) {
        if (!handleException(ex) && mDefaultHandler != null) {
            //如果用户没有处理则让系统默认的异常处理器来处理
            mDefaultHandler.uncaughtException(thread, ex);
        } else {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                Log.e(TAG, "error : ", e);
            }
            //退出程序
            android.os.Process.killProcess(android.os.Process.myPid());
            System.exit(1);
        }
    }

    /**
     * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.
     *
     * @param ex
     * @return true:如果处理了该异常信息;否则返回false.
     */
    private boolean handleException(Throwable ex) {
        if (ex == null) {
            return false;
        }
        //收集设备参数信息
        collectDeviceInfo(mContext);

        //使用Toast来显示异常信息
        new Thread() {
            @Override
            public void run() {
                Looper.prepare();
                Toast.makeText(mContext, "北京新闻程序出现异常,即将退出.", Toast.LENGTH_SHORT).show();
                Looper.loop();
            }
        }.start();
        //保存日志文件
        saveCatchInfo2File(ex);
        return true;
    }

    /**
     * 收集设备参数信息
     * @param ctx
     */
    public void collectDeviceInfo(Context ctx) {
        try {
            PackageManager pm = ctx.getPackageManager();
            PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), PackageManager.GET_ACTIVITIES);
            if (pi != null) {
                String versionName = pi.versionName == null ? "null" : pi.versionName;
                String versionCode = pi.versionCode + "";
                infos.put("versionName", versionName);
                infos.put("versionCode", versionCode);
            }
        } catch (NameNotFoundException e) {
            Log.e(TAG, "an error occured when collect package info", e);
        }
        Field[] fields = Build.class.getDeclaredFields();
        for (Field field : fields) {
            try {
                field.setAccessible(true);
                infos.put(field.getName(), field.get(null).toString());
                Log.d(TAG, field.getName() + " : " + field.get(null));
            } catch (Exception e) {
                Log.e(TAG, "an error occured when collect crash info", e);
            }
        }
    }

    /**
     * 保存错误信息到文件中
     *
     * @param ex
     * @return  返回文件名称,便于将文件传送到服务器
     */
    private String saveCatchInfo2File(Throwable ex) {

        StringBuffer sb = new StringBuffer();
        for (Map.Entry<String, String> entry : infos.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            sb.append(key + "=" + value + "\n");
        }

        Writer writer = new StringWriter();
        PrintWriter printWriter = new PrintWriter(writer);
        ex.printStackTrace(printWriter);
        Throwable cause = ex.getCause();
        while (cause != null) {
            cause.printStackTrace(printWriter);
            cause = cause.getCause();
        }
        printWriter.close();
        String result = writer.toString();
        sb.append(result);
        try {
            long timestamp = System.currentTimeMillis();
            String time = formatter.format(new Date());
            String fileName = "crash-" + time + "-" + timestamp + ".log";
            if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
                String path = Environment.getExternalStorageDirectory()+"/beijingnews/crash/";
                File dir = new File(path);
                if (!dir.exists()) {
                    dir.mkdirs();
                }
                FileOutputStream fos = new FileOutputStream(path + fileName);
                fos.write(sb.toString().getBytes());
                //发送给开发人员
                sendCrashLog2PM(path+fileName);
                fos.close();
            }
            return fileName;
        } catch (Exception e) {
            Log.e(TAG, "an error occured while writing file...", e);
        }
        return null;
    }

    /**
     * 将捕获的导致崩溃的错误信息发送给开发人员
     *
     * 目前只将log日志保存在sdcard 和输出到LogCat中,并未发送给后台。
     */
    private void sendCrashLog2PM(String fileName){
        if(!new File(fileName).exists()){
            Toast.makeText(mContext, "日志文件不存在!", Toast.LENGTH_SHORT).show();
            return;
        }
        FileInputStream fis = null;
        BufferedReader reader = null;
        String s = null;
        try {
            fis = new FileInputStream(fileName);
            reader = new BufferedReader(new InputStreamReader(fis, "GBK"));
            while(true){
                s = reader.readLine();
                if(s == null) break;
                //由于目前尚未确定以何种方式发送,所以先打出log日志。
                Log.i("info", s.toString());
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally{   // 关闭流
            try {
                reader.close();
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
在application中注册
public class BeijingNewsApplication extends Application {
    /**
    所有组件被创建之前执行
     */
    @Override
    public void onCreate() {
        super.onCreate();
        //监听程序异常
        CrashHandler catchHandler = CrashHandler.getInstance();
        catchHandler.init(getApplicationContext());
    }
}    
FragmentManger通过Tag获取Fragment
        //1.得到FragmentManger
        FragmentManager fm = getSupportFragmentManager();
        //2.开启事务
        FragmentTransaction ft= fm.beginTransaction();
        //3.替换
        ft.replace(R.id.fl_main_content,new ContentFragment(), MAIN_CONTENT_TAG);//主页
        ft.replace(R.id.fl_leftmenu, new LeftmenuFragment(), LEFTMENU_TAG);//左侧菜单
        //4.提交
        ft.commit();

//通过Tag获取Fragment
(LeftmenuFragment) getSupportFragmentManager().findFragmentByTag(LEFTMENU_TAG);
SlideMenu的使用
github:
https://github.com/jfeinstein10/SlidingMenu
//1.继承
MainActivity extends SlidingFragmentActivity
//2.设置侧滑布局
setBehindContentView(R.layout.activity_leftmenu);
//3.设置侧滑布局的样式
//设置显示的模式:左侧菜单+主页,左侧菜单+主页面+右侧菜单;主页面+右侧菜单
slidingMenu.setMode(SlidingMenu.LEFT);
//设置滑动模式:滑动边缘,全屏滑动,不可以滑动
slidingMenu.setTouchModeAbove(SlidingMenu.TOUCHMODE_FULLSCREEN);
//设置占据的宽度
DisplayMetrics outmetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(outmetrics);
screeWidth = outmetrics.widthPixels;
screeHeight = outmetrics.heightPixels;
slidingMenu.setBehindOffset((int) (screeWidth*0.625));
自定义viewpager indicator
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.atguigu.beijingnews.activity.GuideActivity">
    <android.support.v4.view.ViewPager
        android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="40dp">
        //小圆点的位置的viewgroup
        <LinearLayout
            android:id="@+id/ll_point_group"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal" />
        //红色的小圆点指示器
        <ImageView
            android:id="@+id/iv_red_point"
            android:background="@drawable/point_red"
            android:layout_width="10dp"
            android:layout_height="10dp" />
    </RelativeLayout>
</RelativeLayout>
//viewpager的datas.size
        for (int i = 0; i < ids.length; i++) {
            //创建点
            ImageView point = new ImageView(this);
/*<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
    <size android:height="10dp" android:width="10dp"/>
    <solid android:color="@android:color/darker_gray"/>
</shape>*/            
            point.setBackgroundResource(R.drawable.point_normal);
            LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(widthdpi,widthdpi);
            if(i !=0){
                //不包括第0个,所有的点距离左边有10个像数
                params.leftMargin = widthdpi;
            }
            point.setLayoutParams(params);
            //添加到线性布局里面
            ll_point_group.addView(point);
        }
//获取最大的两个小圆点间的间隔
//根据View的生命周期,当视图执行到onLayout或者onDraw的时候,视图的高和宽,边距都有了
iv_red_point.getViewTreeObserver().addOnGlobalLayoutListener(new MyOnGlobalLayoutListener());
//监听器
class MyOnGlobalLayoutListener implements ViewTreeObserver.OnGlobalLayoutListener {
        @Override
        public void onGlobalLayout() {
            //记得移除监听器,否则会不停调用
          iv_red_point.getViewTreeObserver()
          .removeGlobalOnLayoutListener(MyOnGlobalLayoutListener.this);
//            间距  = 第1个点距离左边的距离 - 第0个点距离左边的距离
            leftmax = ll_point_group.getChildAt(1).getLeft() - ll_point_group.getChildAt(0).getLeft();
        }
    }
//在vieapger的监听器中实现动画效果
viewpager.addOnPageChangeListener(new MyOnPageChangeListener());
    class MyOnPageChangeListener implements ViewPager.OnPageChangeListener {

        /**
         * 当页面回调了会回调这个方法
         * @param position 当前滑动页面的位置
         * @param positionOffset 页面滑动的百分比
         * @param positionOffsetPixels 滑动的像数
         */
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

            //两点间滑动距离对应的坐标 = 原来的起始位置 +  两点间移动的距离
            int leftmargin = (int) (position*leftmax +  (positionOffset * leftmax));
            //params.leftMargin = 两点间滑动距离对应的坐标,实现动画效果
            RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) iv_red_point.getLayoutParams();
            params.leftMargin = leftmargin;
            iv_red_point.setLayoutParams(params);
        }
        @Override
        public void onPageSelected(int position) {
        }
        @Override
        public void onPageScrollStateChanged(int state) {
        }
    }

高仿知乎专栏

https://github.com/bxbxbai/ZhuanLan.git
SystemClock.sleep和Thread.sleep的区别
Thread.sleep()是java提供的函数。在调用该函数的过程中可能会发生InterruptedException异常。
SystemClock.sleep()是android提供的函数。在调用该函数的过程中不会发生InterruptedException异常,中断事件将要被延迟直到下一个中断事件。Use this function for delays if you do not useThread.interrupt(), as it will preserve the interrupted state of the thread.
ProgressBar常用设置
    <ProgressBar
            android:id="@+id/bar_progressbar"
            style="@style/FloatProgressBar"
            android:layout_width="50dip"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_marginBottom="2dip"
            android:layout_marginLeft="2dip"
            android:layout_marginTop="2dip"
            android:layout_toRightOf="@id/image_line"
            android:indeterminate="false"
            android:indeterminateOnly="false"
            android:max="100"
            android:progress="10">
    </ProgressBar>
        <style name="FloatProgressBar" parent="@android:style/Widget.ProgressBar.Horizontal">
        <item name="android:indeterminateOnly">false</item>
        <item name="android:progressDrawable">@drawable/float_progressbar</item>
        <item name="android:minWidth">14dip</item>
        <item name="android:maxWidth">14dip</item>
        <item name="android:minHeight">14dip</item>
        <item name="android:maxHeight">14dip</item>
    </style>
Thread的中断
         thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (!Thread.currentThread().isInterrupted()) {
                    SystemClock.sleep(250);
                    i += 5;
                    FloatView.this.post(new Runnable() {
                        @Override
                        public void run() {
                            progressBar.setProgress(i);
                            Log.d("meee",getClass()+":\n"+"i:"+i);
                        }
                    });
                    if (i > 100) {
                        i = 0;
                    }
                }
            }
        });
        thread.start();
        //设置中断标志位为true
        thread.interrupt();
自定义Behavior
public class AvatarImageBehavior extends CoordinatorLayout.Behavior<CircleImageView> {

    private final static float MIN_AVATAR_PERCENTAGE_SIZE   = 0.3f;
    private final static int EXTRA_FINAL_AVATAR_PADDING     = 80;

    private final static String TAG = "behavior";
    private final Context mContext;
    private float mAvatarMaxSize;

    private float mFinalLeftAvatarPadding;
    private float mStartPosition;
    private int mStartXPosition;
    private float mStartToolbarPosition;

    private int mStartYPosition;
    private int mFinalYPosition;
    private int finalHeight;
    private int mStartHeight;
    private int mFinalXPosition;


    public AvatarImageBehavior(Context context, AttributeSet attrs) {
        mContext = context;
        mAvatarMaxSize = mContext.getResources().getDimension(R.dimen.image_width);
        mFinalLeftAvatarPadding = 100;
    }

    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, CircleImageView child, View dependency) {
        return dependency instanceof Toolbar;
    }

    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, CircleImageView child, View dependency) {
        shouldInitProperties(child, dependency);

        final int maxScrollDistance = (int) (mStartToolbarPosition - getStatusBarHeight());
        float expandedPercentageFactor = dependency.getY() / maxScrollDistance;

        float distanceYToSubtract = ((mStartYPosition - mFinalYPosition)
            * (1f - expandedPercentageFactor)) + (child.getHeight()/2);

        float distanceXToSubtract = ((mStartXPosition - mFinalXPosition)
            * (1f - expandedPercentageFactor)) + (child.getWidth()/2);

        float heightToSubtract = (mStartHeight - finalHeight) * (1f - expandedPercentageFactor);

        child.setY(mStartYPosition - distanceYToSubtract);
        child.setX(mStartXPosition - distanceXToSubtract);

        int proportionalAvatarSize = (int) (mAvatarMaxSize * (expandedPercentageFactor));

        CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) child.getLayoutParams();
        lp.width = (int) (mStartHeight - heightToSubtract);
        lp.height = (int) (mStartHeight - heightToSubtract);
        child.setLayoutParams(lp);
        return true;
    }

    private void shouldInitProperties(CircleImageView child, View dependency) {
        if (mStartYPosition == 0)
            mStartYPosition = (int) (child.getY() + (child.getHeight() / 2));

        if (mFinalYPosition == 0)
            mFinalYPosition = dependency.getHeight() /2;

        if (mStartHeight == 0)
            mStartHeight = child.getHeight();

        if (finalHeight == 0)
            finalHeight = mContext.getResources().getDimensionPixelOffset(R.dimen.image_final_width);

        if (mStartXPosition == 0)
            mStartXPosition = (int) (child.getX() + (child.getWidth() / 2));

        if (mFinalXPosition == 0)
            mFinalXPosition = mContext.getResources().getDimensionPixelOffset(R.dimen.abc_action_bar_content_inset_material) + (finalHeight / 2);

        if (mStartToolbarPosition == 0)
            mStartToolbarPosition = dependency.getY() + (dependency.getHeight()/2);
    }

    public int getStatusBarHeight() {
        int result = 0;
        int resourceId = mContext.getResources().getIdentifier("status_bar_height", "dimen", "android");

        if (resourceId > 0) {
            result = mContext.getResources().getDimensionPixelSize(resourceId);
        }
        return result;
    }
}
在photoView上添加view
添加权限,此乃高危权限
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
        TextView view=new TextView(this);
        view.setText("你好啊");

        WindowManager wm = (WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE);

        WindowManager.LayoutParams wmParams = new WindowManager.LayoutParams();
        wmParams.type = WindowManager.LayoutParams.TYPE_PHONE;
        wmParams.format = PixelFormat.RGBA_8888;
        wmParams.flags |= 8;
        wmParams.gravity = Gravity.RIGHT | Gravity.CENTER_VERTICAL;
        wmParams.x = 0;
        wmParams.y = 80;
        wmParams.width = ViewGroup.LayoutParams.WRAP_CONTENT;
        wmParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;

        view.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(getApplicationContext(),"你好啊",Toast.LENGTH_SHORT).show();
            }
        });
        //添加到屏幕上后不能再修改view,否则抛异常
        wm.addView(view, wmParams);
判断act是不是第一次启动,第一次添加Fragment
onCreate(savedInstanceState: Bundle?){
       if (savedInstanceState == null) {
            replaceToFlexboxLayoutFragment(fragmentManager)
        }}
FragmentTyTag的使用
private fun replaceToFlexboxLayoutFragment(fragmentManager: FragmentManager) {
//先在这里尝试通过tag获取fragment,为空再创建
        var fragment: FlexboxLayoutFragment? = fragmentManager.findFragmentByTag(FLEXBOXLAYOUT_FRAGMENT) as FlexboxLayoutFragment?
        if (fragment == null) {
            fragment = FlexboxLayoutFragment.newInstance()
        }
        fragmentManager.beginTransaction()
        //在这里传入tag
                .replace(R.id.container, fragment, FLEXBOXLAYOUT_FRAGMENT)
                .commit()
    }
使用Scanner类进行文本的读取
    private String readFile(String fileName) {
        AssetManager manager = getActivity().getAssets();
        try {
            Scanner scanner = new Scanner(manager.open(fileName));
            StringBuilder builder = new StringBuilder();
            while (scanner.hasNext()) {
                builder.append(scanner.nextLine());
            }
            return builder.toString();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "";
    }
自定义控件,对NestedScrollView进行拓展监听滑动距离
//监听滑动距离的ScrollView
public class ObservableScrollView extends NestedScrollView {

    private List<OnScrollListener> listenerList = new ArrayList<>();

    //三个构造器
    public ObservableScrollView(Context context) {
        super(context);
    }
    public ObservableScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    public ObservableScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    /*
     * @param l Current horizontal scroll origin.
     * @param t Current vertical scroll origin.
     * @param oldl Previous horizontal scroll origin.
     * @param oldt Previous vertical scroll origin.
     * */
    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);

        for (OnScrollListener listener : listenerList) {
            listener.onScroll(t - oldt);
        }
    }

    public void addOnScrollListener(OnScrollListener listener) {
        listenerList.add(listener);
    }

    public interface OnScrollListener {
        void onScroll(int distance);
    }
}

WebView的封装

public class CommonWebView extends WebView {

    public static final String ENCODING_UTF_8 = "UTF-8";
    public static final String MIME_TYPE = "text/html";
    //四个构造器都进行初始化操作
    public CommonWebView(Context context) {
        super(context);
        init();
    }
    public CommonWebView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }
    public CommonWebView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }
    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public CommonWebView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init();
    }

    private void init() {
        if (isInEditMode()) {
            return;
        }
        WebSettings settings = getSettings();
        settings.setJavaScriptEnabled(true);
        settings.setBuiltInZoomControls(false);
        //设置缓存模式
        settings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
        //开启DOM storage API功能
        settings.setDomStorageEnabled(true);
        //开启database storage 功能
        settings.setDatabaseEnabled(true);
        //设置缓存
        String cacheDir = getContext().getFilesDir().getAbsolutePath() + "web_cache";
        settings.setAppCachePath(cacheDir);
        settings.setAppCacheEnabled(true);
        //自动加载图片
        settings.setLoadsImagesAutomatically(true);
        //设置编码格式
        settings.setDefaultTextEncodingName(ENCODING_UTF_8);
        //是否不加载图片,只有在setLoadsImagesAutomatically为true是该设置有意义
        settings.setBlockNetworkImage(false);
        //设置布局的算法为单列
        settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
        //是否开启宽视图,一般在手机上都设置为开启
        settings.setUseWideViewPort(true);
        //是否缩小内容以适配屏幕宽度
        settings.setLoadWithOverviewMode(true);
        //关闭滑动条
        setHorizontalScrollBarEnabled(false);
    }
}
public class ZhuanLanWebViewClient extends WebViewClient {
    private Activity mActivity;
    public ZhuanLanWebViewClient(Activity activity) {
        mActivity = activity;
    }
    @Override
    public void onLoadResource(WebView view, String url) {
        super.onLoadResource(view, url);
    }

    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        //拦截所有跳转,判断跳转的方法是不是http开头的,是的话用一个新页面打开
        if (url != null && url.startsWith("orpheus")) {
            return true;
        }
        if (url != null && url.startsWith("http")) {
            WebActivity.start(mActivity, url);
            return true;
        }
        return true;
    }

    @Override
    public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
        return super.shouldInterceptRequest(view, url);
    }

    @Override
    public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
        return super.shouldInterceptRequest(view, request);
    }

    @Override
    public void onPageStarted(WebView view, String url, Bitmap favicon) {
        super.onPageStarted(view, url, favicon);
    }

    @Override
    public void onPageFinished(WebView view, String url) {
        super.onPageFinished(view, url);
    }

    @Override
    public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
        super.onReceivedError(view, errorCode, description, failingUrl);
    }
}
//使用方法
mWebView.setWebViewClient(new ZhuanLanWebViewClient(getActivity()));
//加载html代码
String content = String.format(readFile("template.txt"), section);
mWebView.loadDataWithBaseURL(null, content, "text/html", "UTF-8", null);
//加载js方法
        mWebView.loadUrl("javascript:(function() {" +
                "var parent = document.getElementsByTagName('head').item(0);" +
                "var style = document.createElement('style');" +
                "style.type = 'text/css';" +
                // Tell the browser to BASE64-decode the string into your script !!!
                "style.innerHTML = window.atob('" + encoded + "');" +
                "parent.appendChild(style)" +
                "})()");

当Act打开时数据异常的处理

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.coordinator_template);

        post = getIntent().getParcelableExtra(StoryFragment.KEY_POST);
        //数据异常
        if (post == null) {
        //finish和return oncreate
            finish();
            return;
        }
    }
自定义圆角背景图
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/background_button"/>//一张9.图
    <item android:id="@+id/shape_background">
        <shape>
            <solid android:color="@color/feed_item_bg"/>//#fff
            <corners android:radius="@dimen/feed_item_corner_radius"/>//3dp
        </shape>
    </item>
</layer-list>
Context打开Act
        final Intent intent = new Intent();
        intent.setClass(context, PostListActivity.class);
        intent.putExtra(KEY_USER, userEntity);
        //Handler单例
        CommonExecutor.MAIN_HANDLER.postDelayed(new Runnable() {
            @Override
            public void run() {
                context.startActivity(intent);
            }
        }, 420);
菜单栏的使用
//res/menu/main.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
            android:id="@+id/action_settings"
            android:orderInCategory="10"
            app:showAsAction="never"
            android:title="@string/action_settings"/>

    <item
        android:id="@+id/action_about"
        android:orderInCategory="100"
        android:title="@string/settings_others_about"/>
</menu>
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()){
            case R.id.action_settings:
                Toast.makeText(getApplicationContext(),"action_settings",Toast.LENGTH_SHORT).show();
                break;
            case R.id.action_about:
                Toast.makeText(getApplicationContext(),"action_about",Toast.LENGTH_SHORT).show();
                break;
        }
        return true;
    }
DrawerLayout&NavigationView和Toolbar的使用
<android.support.v4.widget.DrawerLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/drawerLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
     //主内容
    <LinearLayout
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            //toolbar的使用
        <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:background="?attr/colorPrimary"
                app:theme="@style/Base.ThemeOverlay.AppCompat.Dark.ActionBar"/>

        <FrameLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical"
                android:id="@+id/container"/>
    </LinearLayout>
    //侧拉菜单
    <include layout="@layout/drawer_view"/>
</android.support.v4.widget.DrawerLayout>


//初始化
        actionBarDrawerToggle = new ActionBarDrawerToggle(this, drawerLayout, toolbar,
                R.string.app_name, R.string.app_name);
        drawerLayout.addDrawerListener(actionBarDrawerToggle);
        //同步侧拉菜单和toolbar图标
        actionBarDrawerToggle.syncState();
        //toolbar左上角图标的点击事件
        toolbar.setNavigationOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (drawerLayout.isDrawerOpen(GravityCompat.START)) {
                    closeDrawer();
                } else {
                    openDrawer();
                }
            }
        });
//1
<android.support.design.widget.NavigationView
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:background="@color/white"
        app:headerLayout="@layout/drawer_header"
        app:elevation="3dp"
        app:insetForeground="#4000">
    <ListView
        android:id="@+id/drawer_list"
        android:layout_marginTop="200dp"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:listSelector="?android:selectableItemBackground"
        android:drawSelectorOnTop="true"/>
</android.support.design.widget.NavigationView>

//2
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:id="@+id/drawer_layout"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:fitsSystemWindows="true">//设置状态栏是否透明化

     <!-- Your contents -->

     <android.support.design.widget.NavigationView
         android:id="@+id/navigation"
         android:layout_width="wrap_content"
         android:layout_height="match_parent"
         android:layout_gravity="start"//出来的方向
         app:menu="@menu/my_navigation_items"//menu
         app:headerLayout="@layout/nav_header_main" //定制头部
         />
 </android.support.v4.widget.DrawerLayout>
toolbar
        <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"//actionbarheight
                android:background="?attr/colorPrimary"//主配色
                app:theme="@style/Base.ThemeOverlay.AppCompat.Dark.ActionBar"/>

        toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
            @Override
            public boolean onMenuItemClick(MenuItem item) {
                switch (item.getItemId()) {
                    case R.id.action_settings:
                        Tips.showToast("Coming soon...");
                        break;
                    case R.id.action_about:
                        return WebActivity.start(MainActivity.this, WebActivity.URL_BXBXBAI, "About Me");
                }
                return false;
            }
        });
onPostCreate
记得之前想要在Activity布局完成,彻底跑起来之后,再获取当前Activity的窗口中,某个View的宽高,之前用的办法很土,弄个Handler,发个Message出来,使用sendMessageDelayed或者sendEmptyMessageDelayed。
说白了就是延迟若干时间之,等Activity彻底跑起来之后,再取获取View的宽高。
使用这个办法,总是有一种担心:delay的时间太长了,害怕使用这个获取的宽高的值的时候,自己获取宽高的函数还没被调用;delay的时间太短了,又害怕在某些配置比较低的机型上,在delay的时间内Activity没能彻底的跑起来,获取到的值可能会不正确。
//在onResume后执行
使用时钟芯片的做随机数的种子
Random r = new Random(SystemClock.elapsedRealtime());
r.nextInt(SPLASH_ARRAY.length)
真值动画集合
       ObjectAnimator animatorX = ObjectAnimator.ofFloat(mSplashImage, View.SCALE_X, 1f, SCALE_END);
        ObjectAnimator animatorY = ObjectAnimator.ofFloat(mSplashImage, View.SCALE_Y, 1f, SCALE_END);

        AnimatorSet set = new AnimatorSet();
        set.setDuration(ANIMATION_DURATION).play(animatorX).with(animatorY);
        set.start();

        set.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                MainActivity.start(SplashActivity.this);
                SplashActivity.this.finish();
            }
        });
        //以c a b顺序执行动画
        animatorSet3.play(animatorA).after(animatorC).before(animatorB);
getViewTreeObserver
这是一个注册监听视图树的观察者(observer),在视图树种全局事件改变时得到通知。这个全局事件不仅还包括整个树的布局,从绘画过程开始,触摸模式的改变等。ViewTreeObserver不能够被应用程序实例化,因为它是由视图提供,参照getViewTreeObserver()以查看更多信息。
        mSplashImage.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
            @Override
            public boolean onPreDraw() {
                mSplashImage.getViewTreeObserver().removeOnPreDrawListener(this);
                mSplashImage.buildDrawingCache();

                blur(mSplashImage.getDrawingCache(), titleView);
                return true;
            }
        });
获取ImageView的图片缓存
Bitmap bmp = iv.getDrawingCache();

高仿bilibili客户端

github:https://github.com/HotBitmapGG/bilibili-android-client
OkHttp设置缓存
                    //设置Http缓存
                    Cache cache = new Cache(new File(BilibiliApp.getInstance().getCacheDir(), "HttpCache"), 1024 * 1024 * 10);
                    mOkHttpClient = new OkHttpClient.Builder()
                            .cache(cache)
                            .addInterceptor(interceptor)
                            .addNetworkInterceptor(new CacheInterceptor())//请求头
                            .addNetworkInterceptor(new StethoInterceptor())//看日志用
                            .retryOnConnectionFailure(true)//自动重连
                            .connectTimeout(30, TimeUnit.SECONDS)
                            .writeTimeout(20, TimeUnit.SECONDS)
                            .readTimeout(20, TimeUnit.SECONDS)
                            .addInterceptor(new UserAgentInterceptor())//UA
                            .build();
自定义UA
    /**
     * 添加UA拦截器,B站请求API需要加上UA才能正常使用
     */
    private static class UserAgentInterceptor implements Interceptor {
        @Override
        public Response intercept(Chain chain) throws IOException {
            Request originalRequest = chain.request();
            Request requestWithUserAgent = originalRequest.newBuilder()
                    .removeHeader("User-Agent")
                    .addHeader("User-Agent", ApiConstants.COMMON_UA_STR)
                    .build();
            return chain.proceed(requestWithUserAgent);
        }
    }

自定义请求头

    private static class CacheInterceptor implements Interceptor {
        @Override
        public Response intercept(Chain chain) throws IOException {
            // 有网络时 设置缓存超时时间1个小时
            int maxAge = 60 * 60;
            // 无网络时,设置超时为1天
            int maxStale = 60 * 60 * 24;
            Request request = chain.request();
            if (CommonUtil.isNetworkAvailable(BilibiliApp.getInstance())) {
                //有网络时只从网络获取
                request = request.newBuilder().cacheControl(CacheControl.FORCE_NETWORK).build();
            } else {
                //无网络时只从缓存中读取
                request = request.newBuilder().cacheControl(CacheControl.FORCE_CACHE).build();
            }
            Response response = chain.proceed(request);
            if (CommonUtil.isNetworkAvailable(BilibiliApp.getInstance())) {
                response = response.newBuilder()
                        .removeHeader("Pragma")
                        .header("Cache-Control", "public, max-age=" + maxAge)
                        .build();
            } else {
                response = response.newBuilder()
                        .removeHeader("Pragma")
                        .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
                        .build();
            }
            return response;
        }
    }
通过xml保存String[]
//res/values/strings
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array name="sections">
        <item>@string/section_live</item>
        <item>@string/section_recommend</item>
        <item>@string/section_bangumi</item>
        <item>@string/section_more</item>
        <item>@string/section_focus</item>
        <item>@string/section_discover</item>
    </string-array>
</resources>
String[] TITLES== context.getResources().getStringArray(R.array.sections);    
判断网络是否可用
    public static boolean isNetworkAvailable(Context context) {
        NetworkInfo info = getNetworkInfo(context);
        return info != null && info.isAvailable();
    }
ImageButton的使用
            <ImageButton
                android:id="@+id/delete_username"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:background="@android:color/transparent"
                android:src="@drawable/ic_edittext_clear"/>
碎片化的处理
//兼容这五种分辨率
res/values/dimens.xml
res/values-hdpi/dimens.xml
res/values-xhdpi/dimens.xml
res/values-xxhdpi/dimens.xml
res/values-xxxhdpi/dimens.xml
<resources>
    <!-- 不同分辨率下dp sp不同 -->
    <dimen name="activity_horizontal_margin">16dp</dimen>
    <dimen name="default_tiny_text_size">12sp</dimen>
</resources>
//需要配合Drawerlayout使用
<android.support.v4.widget.DrawerLayout
    android:id="@+id/drawer_layout"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">
    <FrameLayout
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    <android.support.design.widget.NavigationView
        android:id="@+id/navigation_view"
        android:layout_width="@dimen/navigation_max_width"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:background="@color/navigation_background_light"
        android:overScrollMode="never"
        app:headerLayout="@layout/layout_navigation_header"
        app:menu="@menu/navigation_main" />
</android.support.v4.widget.DrawerLayout>
    <android.support.design.widget.NavigationView
        android:id="@+id/navigation_view"
        android:layout_width="@dimen/navigation_max_width"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:background="@color/navigation_background_light"
        android:overScrollMode="never"
        //传入侧滑菜单头布局
        app:headerLayout="@layout/layout_navigation_header"
        //下半部分菜单栏部分
        app:menu="@menu/navigation_main" />

//代码:获取到headview
View headerView = mNavigationView.getHeaderView(0);
//res/menu/navigation_main.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <group android:checkableBehavior="single">
        <item
            android:id="@+id/item_home"
            android:checked="true"
            android:icon="@drawable/ic_home_black_24dp"
            android:title="@string/item_home" />
        <item
            android:id="@+id/item_vip"
            android:checkable="false"
            android:icon="@drawable/ic_nav_vip"
            android:title="@string/item_vip" />
        <item
            android:id="@+id/item_download"
            android:checkable="false"
            android:icon="@drawable/ic_file_download_black_24dp"
            android:title="@string/item_downloaded" />
    </group>

    <group
        android:id="@+id/group_user"
        android:checkableBehavior="single">
        <item
            android:id="@+id/item_favourite"
            android:icon="@drawable/ic_star_black_24dp"
            android:title="@string/item_favourite" />
        <item
            android:id="@+id/item_history"
            android:icon="@drawable/ic_history_black_24dp"
            android:title="@string/item_history" />
        <item
            android:id="@+id/item_group"
            android:icon="@drawable/ic_people_black_24dp"
            android:title="@string/item_group" />
        <item
            android:id="@+id/item_tracker"
            android:icon="@drawable/ic_account_balance_wallet_black_24dp"
            android:title="@string/item_tracker" />
    </group>

    <group android:checkableBehavior="single">
        <item
            android:id="@+id/item_theme"
            android:checkable="false"
            android:icon="@drawable/ic_color_lens_black_24dp"
            android:title="@string/item_theme" />
        <item
            android:id="@+id/item_app"
            android:checkable="false"
            android:icon="@drawable/ic_shop_black_24dp"
            android:title="@string/item_app" />
        <item
            android:id="@+id/item_settings"
            android:icon="@drawable/ic_settings_black_24dp"
            android:title="@string/item_settings" />
    </group>
</menu>

//代码部分
mNavigationView.setNavigationItemSelectedListener(this);
    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
        mDrawerLayout.closeDrawer(GravityCompat.START);
        switch (item.getItemId()) {
            case R.id.item_home:
                // 主页
                changeFragmentIndex(item, 0);
                return true;
            case R.id.item_download:
                // 离线缓存
                startActivity(new Intent(MainActivity.this, OffLineDownloadActivity.class));
                return true;
            case R.id.item_vip:
                //大会员
                startActivity(new Intent(MainActivity.this, VipActivity.class));
                return true;
            case R.id.item_favourite:
                // 我的收藏
                changeFragmentIndex(item, 1);
                return true;
            case R.id.item_history:
                // 历史记录
                changeFragmentIndex(item, 2);
                return true;
            case R.id.item_group:
                // 关注的人
                changeFragmentIndex(item, 3);
                return true;
            case R.id.item_tracker:
                // 我的钱包
                changeFragmentIndex(item, 4);
                return true;
            case R.id.item_theme:
                // 主题选择
                return true;
            case R.id.item_app:
                // 应用推荐
                return true;
            case R.id.item_settings:
                // 设置中心
                changeFragmentIndex(item, 5);
                return true;
        }
        return false;
    }
圆角矩形
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid android:color="@color/nav_head_round_background" />
    <corners android:radius="2dp" />
</shape>
切换到夜间模式
    // 日间模式
    AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
    // 夜间模式
    AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
    //切换后调用
    Acitivity.recreate();

    在控件中使用与夜间模式相关的属性
    android:background="@color/colorPrimary"
判断DrawerLayout是否打开
//如果已经打开,则关闭
  if (mDrawerLayout.isDrawerOpen(mDrawerLayout.getChildAt(1))) {
       mDrawerLayout.closeDrawers();
   }
双击退出
    private long exitTime;
    private void exitApp() {
        if (System.currentTimeMillis() - exitTime > 2000) {
            ToastUtil.ShortToast("再按一次退出");
            exitTime = System.currentTimeMillis();
        } else {
            PreferenceUtil.remove(ConstantUtil.SWITCH_MODE_KEY);
            finish();
        }
    }
自定义EmptyView
public class CustomEmptyView extends FrameLayout {
    private ImageView mEmptyImg;
    private TextView mEmptyText;

    //三种构造器
    public CustomEmptyView(Context context) {
        this(context, null);
    }
    public CustomEmptyView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }
    public CustomEmptyView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
        setEmptyViewVisibility(true);
    }
    //
    public void init() {
        //在这里相当于把控件add了
        View view = LayoutInflater.from(getContext()).inflate(R.layout.layout_empty, this);
        mEmptyImg = (ImageView) view.findViewById(R.id.empty_img);
        mEmptyText = (TextView) view.findViewById(R.id.empty_text);
    }
    public void setEmptyImage(int imgRes) {
        mEmptyImg.setImageResource(imgRes);
    }
    public void setEmptyText(String text) {
        mEmptyText.setText(text);
    }
    public void setEmptyViewVisibility(boolean isVisible){
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            if (i==0){
                getChildAt(i).setVisibility(isVisible?VISIBLE:GONE);
            }else{
                getChildAt(i).setVisibility(isVisible?GONE:VISIBLE);
            }
        }
    }

    public void addContentView(@LayoutRes int layoutId){
        LayoutInflater.from(getContext()).inflate(layoutId, this);
    }
}
手机磁盘大小的获取及其格式化
    /**
     * 获取手机内部存储总空间
     */
    public static long getPhoneTotalSize() {
        if (!checkSdCard()) {
            File path = Environment.getDataDirectory();
            StatFs mStatFs = new StatFs(path.getPath());
            long blockSizeLong = mStatFs.getBlockSizeLong();
            long blockCountLong = mStatFs.getBlockCountLong();
            return blockSizeLong * blockCountLong;
        } else {
            return getSDcardTotalSize();
        }
    }
    /**
     * 获取手机内存存储可用空间
     */
    public static long getPhoneAvailableSize() {
        if (!checkSdCard()) {
            File path = Environment.getDataDirectory();
            StatFs mStatFs = new StatFs(path.getPath());
            long blockSizeLong = mStatFs.getBlockSizeLong();
            long availableBlocksLong = mStatFs.getAvailableBlocksLong();
            return blockSizeLong * availableBlocksLong;
        } else
            return getSDcardAvailableSize();
    }
        //转换为G的显示单位
        String totalSizeStr = Formatter.formatFileSize(this, phoneTotalSize);
        String availabSizeStr = Formatter.formatFileSize(this, phoneAvailableSize);

高仿谷歌市场

recyclerView的多类型布局处理
public class HotAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int type) {
        if (type == BANNER) {
            BannerItemHodler hodler = new BannerItemHodler(inflater.inflate(R.layout.item_banner, null));
            return hodler;
        } else {
            HotItemHodler hodler = new HotItemHodler(inflater.inflate(R.layout.item_live, null),clickListener);
            return hodler;
        }
    }
    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int i) {
        if (viewHolder instanceof HotItemHodler) {
            LivesBean bean = dates.get(i - 1);
            try {
                setHolder(bean, (HotItemHodler) viewHolder);
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        } else if (viewHolder instanceof BannerItemHodler) {
            setBaner(image,(BannerItemHodler)viewHolder);
        }
    }
设置et的hint的字体颜色
        <EditText
            android:id="@+id/search"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_toRightOf="@id/searchicon"
            android:background="@null"
            android:hint="请输入昵称/映客号"
            android:textColorHint="@android:color/darker_gray"
            android:singleLine="true"
            android:textSize="15sp"
            />
        search.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {

            }

            @Override
            public void afterTextChanged(Editable s) {
                //如果搜索的关键字不为空,就去请求查询接口
                if (!TextUtils.isEmpty(s.toString())) {
                    clean.setVisibility(View.VISIBLE);
                    doSearch(s.toString());
                } else {
                    clean.setVisibility(View.GONE);
                    result.setVisibility(View.GONE);
                }
            }
        });
实现tabLayout的效果
        //初始化title
        LayoutInflater inflater = LayoutInflater.from(getContext());
        View view = inflater.inflate(R.layout.include_title,tabs,false);
        leftSearch=(ImageView)view.findViewById(R.id.left);

        scrollView = (HorizontalScrollView) view.findViewById(R.id.h_s);
        LinearLayout line = (LinearLayout) view.findViewById(R.id.line);
        for(int i=0;i<infors.size();i++){
            FragmentInfor info = infors.get(i);
            //名字
            View item_view =  inflater.inflate(R.layout.item_title,null);
            TextView title= (TextView) item_view.findViewById(R.id.item_title);
            title.setText(info.getName());
            //linearlayout.addview时itemview具有layoutparams
            LinearLayout.LayoutParams p = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT);
            p.setMargins(40,0,40,0);
            line.addView(item_view,p);
            title.setTag(i);
            title.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    int current = viewPager.getCurrentItem();
                    int position = (int) v.getTag();
                    if(current==position){
                        return;
                    }
                    scrollView.smoothScrollTo(position*188-(scrollView.getWidth()/2-94),0);
                    viewPager.setCurrentItem(position);
                }
            });
            titles.add(item_view);
        }
        tabs.addView(view);
//viewpagerListener
    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        //Log.i("hked","position = "+position+" positionOffset = "+positionOffset+" positionOffsetPixels = "+positionOffsetPixels );
        if(positionOffset == 0){
            //在这里获取都title的中itemview的实例,设置动画效果
            for(int i =0;i<titles.size();i++){
                View view = titles.get(i);
                if(i!=position){
                    view.setScaleX(1f);
                    view.setScaleY(1f);
                }else{
                    view.setScaleX(1.2f);
                    view.setScaleY(1.2f);
                }
            }
            return;
        }
        View view = null;
        View next = null;
        if(positionOffsetPixels>lastP){
            //往右
            next = titles.get(position+1);
            next.setScaleX(1f+(0.2f*(positionOffset)));
            next.setScaleY(1f+(0.2f*(positionOffset)));

            view = titles.get(position);
            view.setScaleX(1.2f-(0.2f*(positionOffset)));
            view.setScaleY(1.2f-(0.2f*(positionOffset)));
        }else if(positionOffsetPixels<lastP){
            //往左
            next = titles.get(position);
            next.setScaleX(1f+(0.2f*(1f-positionOffset)));
            next.setScaleY(1f+(0.2f*(1f-positionOffset)));

            view = titles.get(position+1);
            view.setScaleX(1.2f-(0.2f*(1f-positionOffset)));
            view.setScaleY(1.2f-(0.2f*(1f-positionOffset)));
        }
        lastP = positionOffsetPixels;
    }

    @Override
    public void onPageSelected(int position) {

       if(position == 1){
           View view = titles.get(1);
           ImageView down = (ImageView) view.findViewById(R.id.down);
           down.setVisibility(View.VISIBLE);
       }else{
           View view = titles.get(1);
           ImageView down = (ImageView) view.findViewById(R.id.down);
           down.setVisibility(View.GONE);
       }
        scrollView.smoothScrollTo(position*188-(scrollView.getWidth()/2-94),0);
    }

    @Override
    public void onPageScrollStateChanged(int state) {

    }
Fragment的实例化处理
public class FragmentInfor {
    private String name;
    private Fragment fragment;
    public FragmentInfor(String name, Fragment fragment) {
        this.name = name;
        this.fragment = fragment;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Fragment getFragment() {
        return fragment;
    }
    public void setFragment(Fragment fragment) {
        this.fragment = fragment;
    }
}
Class [] classes =  {FocusFragment.class,HotFragment.class,NearFragment.class,TalentFragment.class,TalentFragment.class,TalentFragment.class,TalentFragment.class,TalentFragment.class};

        for(int i =0;i<titles.length;i++){
            String title = titles[i];
            //系统的实例化
            Fragment fragment = Fragment.instantiate(getContext(),classes[i].getName());
            FragmentInfor infor = new FragmentInfor(title,fragment);
            infors.add(infor);
        }
自定义提示小圆点
public class RingTextView extends TextView {
    public RingTextView(Context context) {
        super(context);
    }
    public RingTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    @Override
    public void draw(Canvas canvas) {
        Paint circlePaint = new Paint();
        circlePaint.setColor(ContextCompat.getColor(getContext(), R.color.top_mes_back));
        circlePaint.setFlags(Paint.ANTI_ALIAS_FLAG);

        //获取tv的宽高
        int  h = this.getHeight();
        int  w = this.getWidth();
        //设置宽高
        int diameter =Math.max(h,w);
        this.setHeight(diameter);
        this.setWidth(diameter);
        //画圆
        int radius = diameter/2;
        canvas.drawCircle(diameter / 2, diameter / 2, radius, circlePaint);
        super.draw(canvas);
    }
}
Fragment的处理
    private void addMine(FragmentManager fragmentManager, MineFragment mineFragment) {
        FragmentTransaction transaction = fragmentManager.beginTransaction();
        transaction.add(R.id.top, mineFragment, MINE);
        transaction.commit();
    }
    private void showMine(FragmentManager fragmentManager, MineFragment mineFragment) {
        FragmentTransaction transaction = fragmentManager.beginTransaction();
        transaction.show(mineFragment);
        transaction.commit();
    }
    private void hideMine(FragmentManager fragmentManager, MineFragment mineFragment) {
        FragmentTransaction transaction = fragmentManager.beginTransaction();
        transaction.hide(mineFragment);
        transaction.commit();
    }
                FragmentManager fragmentManager = getSupportFragmentManager();
                MineFragment mineFragment = 
                (MineFragment) fragmentManager.findFragmentByTag(MINE);
                LiveFragment liveFragment = 
                (LiveFragment) fragmentManager.findFragmentByTag(LIVE);
                if (null != liveFragment) {
                    hideLive(fragmentManager, liveFragment);
                }
                if (null == mineFragment) {
                    mineFragment = new MineFragment();
                    addMine(fragmentManager, mineFragment);
                } else {
                    showMine(fragmentManager, mineFragment);
                }
selector的处理
//将点击事件交给viewgroup处理
        <LinearLayout
            android:id="@+id/live"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_marginRight="35dp"
            android:layout_weight="1"
            android:clickable="true"
            android:gravity="center"
            android:orientation="horizontal">
            <ImageView
                android:layout_width="35dp"
                android:layout_height="35dp"
                android:background="@drawable/tab_live_selector"/>
        </LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_selected="true" android:drawable="@drawable/tab_live_p"/>
    <item android:state_pressed="true" android:drawable="@drawable/tab_live_p"/>
    <item android:state_focused="true" android:drawable="@drawable/tab_live_p"/>
    <item android:drawable="@drawable/tab_live"/>
</selector>
官方侧滑菜单
compile 'com.android.support:appcompat-v7:25.3.1'
<android.support.v4.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
//侧滑菜单部分
    <FrameLayout
        android:layout_width="180dp"
        android:layout_height="match_parent"
        android:layout_gravity="left"
        android:background="#f00"
        />
//内容部分
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <com.astuetz.PagerSlidingTabStrip
            android:id="@+id/tabs"
            android:layout_width="match_parent"
            android:layout_height="48dip"
            app:pstsIndicatorHeight="1dp"
            app:pstsIndicatorColor="#f00"/>
        <android.support.v4.view.ViewPager
            android:id="@+id/main_vp"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    </LinearLayout>
</android.support.v4.widget.DrawerLayout>
mDrawerLayout = (DrawerLayout) findViewById(R.id.activity_main);
//获取到返回键
ActionBarDrawerToggle mToggle = new ActionBarDrawerToggle(this, mDrawerLayout, R.string.open,R.string.close);
//将侧滑菜单和返回按键绑定
mToggle.syncState();
mDrawerLayout.setDrawerListener(mToggle);

    //复写actiobar里面的箭头回退和出来时间
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case android.R.id.home:
                mToggle.onOptionsItemSelected(item);
                break;
        }
        return super.onOptionsItemSelected(item);
    }
线程池
public class ThreadPoolFactory {

    // 普通下载线程池
    private static ThreadPoolProxy ThreadPoolProxyNormal;
    // 专门用来下载的线程
    private static ThreadPoolProxy ThreadPoolProxyDownLoad;

    //创建一个普通线程池
    public static  ThreadPoolProxy cureatThreadPoolProxyNormal(){
        if (ThreadPoolProxyNormal == null){
            ThreadPoolProxyNormal = new ThreadPoolProxy(3,3,3000);
        }
        return  ThreadPoolProxyNormal;
    }

    //创建一个固定任务的线程池
    public  static  ThreadPoolProxy cureatThreadPoolProxyDownLoad(){
        if (ThreadPoolProxyDownLoad == null){
            ThreadPoolProxyDownLoad = new ThreadPoolProxy(3,3,3000);
        }
        return  ThreadPoolProxyDownLoad;
    }
}
//创建一个线程池
public class ThreadPoolProxy {

    private int mCorePoolSize;  //核心线程的个数
    private int mMaximumPoolSize; //总的线程个数
    private long mKeepAliveTime; //线程空闲的时间
    private ThreadPoolExecutor mPoolExecutor;

    public ThreadPoolProxy(int corePoolSize, int maximumPoolSize, long keepAliveTime) {
        mCorePoolSize = corePoolSize;
        mMaximumPoolSize = maximumPoolSize;
        mKeepAliveTime = keepAliveTime;
    }

    //初始化线程池
    public void initThreadPoolExecutor(){
        //判断不能重复创建
        if (mPoolExecutor ==null || mPoolExecutor.isShutdown() || mPoolExecutor.isTerminated()){
            //同步锁
            synchronized (ThreadPoolProxy.class){
                //创建参数
                TimeUnit unit  = TimeUnit.MICROSECONDS; //毫秒
                BlockingQueue<Runnable> workQueue = new LinkedBlockingDeque<>();
                ThreadFactory threadFactory = Executors.defaultThreadFactory();
                RejectedExecutionHandler handler = new ThreadPoolExecutor.DiscardPolicy();
                mPoolExecutor = new ThreadPoolExecutor(
                        mCorePoolSize,
                        mMaximumPoolSize,
                        mKeepAliveTime,
                        unit, //时间的单位
                        workQueue,//任务队列
                        threadFactory,//线程的工厂
                        handler);  //异常捕获器
            }
        }
    }

    //提交任务的方法
    public  void submit(Runnable task){
        //先初始化线程池
        initThreadPoolExecutor();
        mPoolExecutor.submit(task);
    }

    //执行任务的方法
    public  void cxecute(Runnable task){
        //先初始化线程池
        initThreadPoolExecutor();
        mPoolExecutor.execute(task);
    }

    //移除任务的方法
    public  void remove(Runnable task){
        //先初始化线程池
        initThreadPoolExecutor();
        mPoolExecutor.remove(task);
    }
}

某马手机卫士…不是很有必要看

打开设备管理器,激活高级权限
public class MyDevice extends DeviceAdminReceiver {
}
DevicePolicyManager manager = (DevicePolicyManager) getSystemService(DEVICE_POLICY_SERVICE);
mComponentName = new ComponentName(this,MyDevice.class);

//激活权限
Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, mComponentName);
intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION,
                            "快来激活软件,否则罚款");
startActivity(intent);

//取消激活
manager.removeActiveAdmin(mComponentName);
获取联系人
public class ContactsUtils {
    public static List<Contact> getContacts(Context context){
        List<Contact> lists=new ArrayList<>();
        //查询数据库
          // 1.1 解析器
        ContentResolver resolver = context.getContentResolver();
        // 1.2 准备 uri  content://主界面/路径
        Uri contactUri=Uri.parse("content://com.android.contacts/contacts");
        Uri uri=Uri.parse("content://com.android.contacts/data");

        //查询联系人分为2步
          //1 获得联系人的 id  查询 contact
          //2 通过id获得号码和联系人名称 data--->viewdata

       //通过解析器查询                        查询的URI     查询的字段          查询的条件  条件值
        Cursor contactCursor = resolver.query(contactUri, new String[]{"_id"}, null, null, null);
        while (contactCursor.moveToNext()){
            String id = contactCursor.getString(0);
            //根据id查询data表得到 data1 里面的内容
            if(!TextUtils.isEmpty(id)) {
                Contact contact=new Contact();
                Cursor cursor = resolver.query(uri, new String[]{"mimetype", "data1"}, "raw_contact_id=?", new String[]{id}, null);
                while (cursor.moveToNext()){
                    String mimetype = cursor.getString(0);
                    String data1 = cursor.getString(1);
                    if("vnd.android.cursor.item/phone_v2".equals(mimetype)) {
                        contact.phone=data1;
                    }else  if("vnd.android.cursor.item/name".equals(mimetype)) {
                        contact.name=data1;
                    }
                }
                lists.add(contact);
            }
        }
        return lists;
    }
}
手势识别,总算有个不错的
public abstract class BaseSetupActivity extends Activity {
    //手势识别器
    private GestureDetector detector;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        initView();
        initData();
        initEvent();
        detector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() {
            /**
             * @param e1 事件1 开始手指放下去的事件
             * @param e2 事件2 手指离开的事件
             * @param velocityX x轴的方向的分量
             * @param velocityY y轴方向的分量
             * @return
             */
            @Override
            public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
                //点击next
                if (e1.getRawX() - e2.getRawX() > 20) {
                    showNext();
                    //页面跳转动画
                    overridePendingTransition(R.anim.next_enter, R.anim.next_exit);
                }
                //点击pre
                if (e2.getRawX() - e1.getRawX() > 20) {
                    showPre();
                    //页面跳转动画
                    overridePendingTransition(R.anim.pre_enter, R.anim.pre_exit);
                }
                return super.onFling(e1, e2, velocityX, velocityY);
            }
        });

    }
    /**
     * 所有的屏幕触摸事件最先激活的,或者接受的方法是onTouchEvent
     * @param event 就是触摸屏幕的时间
     * @return
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        detector.onTouchEvent(event);
        return super.onTouchEvent(event);
    }
    //生命周期细分
    protected abstract void initView();
    protected abstract void initData();
    protected abstract void initEvent();
    //左右滑动事件
    protected abstract void showNext();
    protected abstract void showPre();
}
//next_enter
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
           android:duration="300"
           android:fromXDelta="100%p"
           android:fromYDelta="0"
           android:toXDelta="0"
           android:toYDelta="0"/>

//next_exit
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
           android:duration="300"
           android:fromXDelta="0"
           android:fromYDelta="0"
           android:toXDelta="-100%p"
           android:toYDelta="0"/>

//pre_enter
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
           android:duration="300"
           android:fromXDelta="-100%p"
           android:fromYDelta="0"
           android:toXDelta="0"
           android:toYDelta="0"/>

//pre_exit
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
           android:duration="300"
           android:fromXDelta="0"
           android:fromYDelta="0"
           android:toXDelta="100%p"
           android:toYDelta="0"/>
自定义属性
//res/values/attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="MyRelative">
     <attr name="mytext" format="string"></attr>
        <attr name="myType">
            <enum name="first" value="0" />
            <enum name="middle" value="1" />
            <enum name="last" value="2" />
        </attr>
    </declare-styleable>
</resources>
public class MyRelative extends RelativeLayout {

    private RelativeLayout mSet_rl;
    private TextView mSet_name;
    private ImageView mSet_update;
    private boolean isOpen;

    public MyRelative(Context context) {
        this(context, null);
    }

    public MyRelative(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView(context);
        initData(attrs);
    }

    private void initView(Context context) {
        View.inflate(context, R.layout.view_rl, this);
        mSet_rl = (RelativeLayout) findViewById(R.id.set_item_update);
        mSet_name = (TextView) findViewById(R.id.set_item_tv_name);
        mSet_update = (ImageView) findViewById(R.id.set_item_iv_update);
    }

    public void setImageViewState(boolean isOpen) {
        this.isOpen = isOpen;
        if (isOpen) {
            mSet_update.setImageResource(R.mipmap.on);
        } else {
            mSet_update.setImageResource(R.mipmap.off);
        }
    }

    //通过判断自定义的属性来设置背景
    private void initData(AttributeSet attrs) {
        //获取自定义的属性
        String mytext = attrs.getAttributeValue("http://schemas.android.com/apk/res-auto", "mytext");
        mSet_name.setText(mytext);
        String myType = attrs.getAttributeValue("http://schemas.android.com/apk/res-auto", "myType");
        switch (Integer.parseInt(myType)) {
            case 0:
                mSet_rl.setBackgroundResource(R.drawable.set_item_first_backgtound);
                break;
            case 1:
                mSet_rl.setBackgroundResource(R.drawable.set_item_middle_backgtound);
                break;
            case 2:
                mSet_rl.setBackgroundResource(R.drawable.set_item_last_backgtound);
                break;
        }
    }
}
    <com.heima.mobilsafe.view.MyRelative
        gaga:mytext="开启自动更新"
        android:layout_marginTop="20dp"
        android:id="@+id/myrl_update"
        gaga:myType="first"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        />
AlertDialog不需要继承实现自定义view的方法
        AlertDialog.Builder builder=new AlertDialog.Builder(this);
        View view = View.inflate(getApplicationContext(), R.layout.view_set_password, null);
        mEt_password = (EditText) view.findViewById(R.id.view_et_setpassword);
        mEt_conf_password = (EditText) view.findViewById(R.id.view_et_comf_setpassword);
        mBtn_cancel = (Button) view.findViewById(R.id.view_btn_cancel);
        mBtn_ok = (Button) view.findViewById(R.id.view_btn_ok);
        //取消的点击事件
        mBtn_cancel.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mDialog.dismiss();
            }
        });
        mBtn_ok.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 1 获取值
                String passwprd = mEt_password.getText().toString().trim();
                String passwrod_conf = mEt_conf_password.getText().toString().trim();
                // 2 非空判断
                if(TextUtils.isEmpty(passwprd)||TextUtils.isEmpty(passwrod_conf)) {
                    Toast.makeText(HomeActivity.this, "请按要求输入", Toast.LENGTH_SHORT).show();
                    // 3 判断密码是否相等
                }else if(passwprd.equals(passwrod_conf)) {
                    SpUtils.setString(getApplicationContext(),Constants.PASSWORD,passwprd);
                    // 4 保存且关闭对话框
                    mDialog.dismiss();

                }else{
                    Toast.makeText(HomeActivity.this, "密码输入不一致", Toast.LENGTH_SHORT).show();
                }

            }
        });
        builder.setView(view);
        mDialog = builder.show();
无限动画
        //设置旋转动画
        ObjectAnimator rotation = ObjectAnimator.ofFloat(mHome_logo, "rotationY", 0, 30, 90, 120, 160, 190, 260, 360);
        //设置动画永远重复
        rotation.setRepeatCount(ObjectAnimator.INFINITE);
        //动画作用时常
        rotation.setDuration(2000);
        //开启动画
        rotation.start();
TextView文字滚动
            <TextView
                android:id="@+id/music_name_tv"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:ellipsize="marquee"   【必须】
                android:focusable="true"      【必须】
                android:focusableInTouchMode="true" 【必须】
                android:lines="1"              【必须】
                android:text="我的中国心我的中国心我的中国心我的中国心我的中国心我的中国心我的中国心我的中国心我的中国心xxxx"
                android:textColor="@color/colorAccent"
                android:textSize="15sp" />
    //动态赋值后需要重新设置属性
    public static void setTextMarquee(TextView textView) {
        if (textView != null) {
            textView.setEllipsize(TextUtils.TruncateAt.MARQUEE);
            textView.setSingleLine(true);
            textView.setSelected(true);
            textView.setFocusable(true);
            textView.setFocusableInTouchMode(true);
        }
    }
FragmentManager对应的生命周期
add(R.id.content,fristFragment,"handsome");->添加到指定的父控件,一层叠加一层
remove();把一个Fragment彻底删除onDetach

replace(R.id.content,fristFragment,"handsome");

detach()解绑,onDestroyView
attach()重新绑定 onCreateView

show()不会调用任何生命周期方法
hide()
属性动画重要属性
public static ValueAnimator ofFloat(float... values) 
public static ValueAnimator ofInt(int... values)

* public final class ObjectAnimator extends ValueAnimator

public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) 
public static <T> ObjectAnimator ofInt(T target, Property<T, Integer> property, int... values)
android 3.0也可以执行属性动画
使用nineoldandroids-2.2.0.jar
AnimationSet
        //三种动画 动画 属性动画 补间动画(影子动画)

        // 旋转动画                        开始的角度   结束的角度 锚点的相对值           x轴的一半
        RotateAnimation rt = new RotateAnimation(0.0f, 360.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
        //动画作用时常
        rt.setDuration(2000);
        //动画完成后保存完成后的转台或者位置,默认是true
        rt.setFillAfter(true);
        // 缩放动画
        ScaleAnimation sa = new ScaleAnimation(0.0f, 1.0f, 0.0f, 1.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
        sa.setDuration(2000);
        // 透明度渐变动画
        AlphaAnimation at = new AlphaAnimation(0.0f, 1.0f);
        at.setDuration(2000);
        // 动画集
        mSet = new AnimationSet(false);
        mSet.addAnimation(rt);
        mSet.addAnimation(sa);
        mSet.addAnimation(at);
        //开启动画
        mSplash_root.startAnimation(mSet);
使用Xutils实现apk下载和安装
        HttpUtils httpUtils=new HttpUtils();
        //通过xutils下载app
        final File file=new File(Environment.getExternalStorageDirectory(),"xxx.apk");
        httpUtils.download(data.downloadurl, file.getAbsolutePath(), false, new RequestCallBack<File>() {
            @Override
            public void onSuccess(ResponseInfo<File> responseInfo) {
                //下载成功
                Toast.makeText(SplashActivity.this, "下载成功", Toast.LENGTH_SHORT).show();
                //安装
             /*     <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:scheme="content" />
                <data android:scheme="file" />
                <data android:mimeType="application/vnd.android.package-archive" />*/
                Intent intent=new Intent();
                intent.setAction("android.intent.action.VIEW");
                intent.addCategory("android.intent.category.DEFAULT");
                intent.setDataAndType(Uri.fromFile(file),"application/vnd.android.package-archive");
                startActivityForResult(intent,1);
            }
            @Override
            public void onFailure(HttpException e, String s) {
                   //下载失败
            }
        });
InputStream转换成String
    //解析流得到String
    private String paramsStream(InputStream in) throws IOException {
        StringBuffer sb=new StringBuffer();
        BufferedReader reader=new BufferedReader(new InputStreamReader(in));
        String line = reader.readLine();
        while (line!=null){
            sb.append(line);
            line= reader.readLine();
        }
        return sb.toString();
    }

山寨网易newsClient

利用space来为linearlayout占据位置
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:orientation="horizontal">
        <ImageButton
            android:src="@drawable/news_title_back"
            android:background="@drawable/bg_btn_back"
            android:layout_width="50dp"
            android:layout_height="match_parent"
            />
            //一个空白
        <Space
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="wrap_content"/>
        <ImageButton
            android:background="@drawable/bg_btn_back"
            android:src="@drawable/news_title_menu"
            android:layout_width="50dp"
            android:layout_height="match_parent"
            />
    </LinearLayout>
自定义广告轮播图控件
//资源文件

//R.layout.view_banner
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="180dp">

    <android.support.v4.view.ViewPager
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/viewPager_banner"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_vertical"
        android:orientation="horizontal"
        android:layout_alignParentBottom="true"
        android:paddingLeft="8dp"
        android:paddingBottom="8dp"
        android:paddingRight="8dp"
        android:background="@drawable/bg_banner_bottom"
        >

    <TextView
        android:id="@+id/tv_banner"
        android:layout_width="0dp"
        android:layout_weight="1"
        android:layout_height="wrap_content"
        android:textColor="@color/colorWhite"
        android:text="我是轮播图标题"/>

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/ll_dot"
        android:orientation="horizontal"/>

    </LinearLayout>
</RelativeLayout>

//R.drawable.bg_dot
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
    <size android:width="8dp" android:height="8dp"/>
    <corners android:radius="6dp"/>
    <solid android:color="@color/colorWhite"/>
</shape>

//R.drawable.bg_selected
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
    <size android:width="8dp" android:height="8dp"/>
    <corners android:radius="6dp"/>
    <solid android:color="@color/colorGray"/>
</shape>
public class BannerView extends RelativeLayout {

    private int mPicSize;//轮播图所有图片的size
    private Handler mHandler;
    private BannerPagerAdapter mBannerPagerAdapter;

    public BannerView(Context context) {
        super(context);
        init();
    }

    //不要直接传NewsItemBean过来,传一些String过来,这样可以保证该轮播图可以在其他地方也能用
    /*public BannerView(Context context, NewsItemBean bannerData) {
        super(context);
    }*/

    private ArrayList<String> mPicUrls;
    private ArrayList<String> mTitles;
    private ViewPager mViewPagerBanner;
    private TextView mTvBanner;
    private LinearLayout mLlDot;

    public BannerView(Context context, ArrayList<String> picUrls, ArrayList<String> titles) {
        super(context);
        mPicUrls = picUrls;
        mTitles = titles;
        mPicSize = mPicUrls.size();
        init();
    }

    //一般这样来实现控件,是通过将某个xml布局挂载到当前自定义控件上,注意跟布局不能冲突
    //1 找控件出来
    //2 给ViewPager设置PagerAdapter
    //3 传入不是图片地址,而是ImageView的集合(比较有话,就不用各种反复new了)
    //4 初始化小点(添加小点到ll_dot中)
    //5 设置文本控件的text
    private void init() {
        View inflate = View.inflate(getContext(), R.layout.view_banner, this);
        mViewPagerBanner = (ViewPager) inflate.findViewById(R.id.viewPager_banner);
        mTvBanner = (TextView) inflate.findViewById(R.id.tv_banner);
        mLlDot = (LinearLayout) inflate.findViewById(R.id.ll_dot);

        ArrayList<ImageView> imageViews = new ArrayList<>();
        for (int i = 0; i < mPicSize; i++) {
            ImageView imageView = new ImageView(getContext());
            imageView.setScaleType(ImageView.ScaleType.FIT_XY);
            //去加载图片
            ImageUtil.getSingleton().display(mPicUrls.get(i), imageView);
            imageViews.add(imageView);
        }
        //第一次初始化时,直接创建一个BannerPagerAdapter
        mBannerPagerAdapter = new BannerPagerAdapter(imageViews);
        mViewPagerBanner.setAdapter(mBannerPagerAdapter);
        MyPageChangeListener myPageChangeListener = new MyPageChangeListener();
        mViewPagerBanner.addOnPageChangeListener(myPageChangeListener);

        mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                int currentItem = mViewPagerBanner.getCurrentItem();
                mViewPagerBanner.setCurrentItem(currentItem + 1);
                mHandler.sendEmptyMessageDelayed(0, 2000);
            }
        };
        initDot();
    }


    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        int action = ev.getAction();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                mHandler.removeCallbacksAndMessages(null);
                break;
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                mHandler.sendEmptyMessageDelayed(0,2000);
                break;
            default:
                break;
        }
        return super.dispatchTouchEvent(ev);
    }

    //4 初始化小点(添加小点到ll_dot中)
    private void initDot() {
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(-2,
                ViewGroup.LayoutParams.WRAP_CONTENT);
        params.setMargins(0, 0, 10, 0);
        for (int i = 0; i < mPicSize; i++) {
            ImageView imageView = new ImageView(getContext());
            //设置为小点一样的背景图
            imageView.setImageResource(R.drawable.bg_dot);
            //通过LayoutParams设置点之间的间距
            mLlDot.addView(imageView, params);
        }
//        selectDot(0);
        //需要让位置处于中间的取值区域,但是需要保证第一张图片是一开始被选中的
        //100
        //3
        //0 3 6 9 12
        //(50-50%3)= 48   48 51
        //一个数 -一个数除以y的余数 = y的整数倍
        mViewPagerBanner.setCurrentItem((Integer.MAX_VALUE / 2) - Integer.MAX_VALUE / 2 % mPicSize);

        //开始使用Handler发消息
        mHandler.sendEmptyMessageDelayed(0, 2000);
    }

    //选中特定位置的小点,并且修改文本为对应位置的文本
    private void selectDot(int i) {
        mTvBanner.setText(mTitles.get(i));
        int childCount = mLlDot.getChildCount();
        for (int j = 0; j < childCount; j++) {
            ImageView child = (ImageView) mLlDot.getChildAt(j);
            if (j == i) {
                child.setImageResource(R.drawable.bg_dot_selected);
            } else {
                child.setImageResource(R.drawable.bg_dot);
            }
        }
    }

    public BannerView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    //更新数据
    public void updateData(ArrayList<String> picUrls, ArrayList<String> titles) {
        //需要更新轮播图的图片和标题
        mPicUrls = picUrls;
        mTitles = titles;
        mPicSize = mPicUrls.size();

        ArrayList<ImageView> imageViews = new ArrayList<>();
        for (int i = 0; i < mPicSize; i++) {
            ImageView imageView = new ImageView(getContext());
            imageView.setScaleType(ImageView.ScaleType.FIT_XY);
            //去加载图片
            ImageUtil.getSingleton().display(mPicUrls.get(i), imageView);
            imageViews.add(imageView);
        }

        //更新有两种方案:一个是重新setAdapter,
        // 还有一个,对原有的adapter中数据list集合做改变,然后notify(推荐)
        //这里更新的时候,不再new Adapter了,采用notify
        if(mBannerPagerAdapter!=null){
            mBannerPagerAdapter.updateData(imageViews);
        }
//        BannerPagerAdapter bannerPagerAdapter = new BannerPagerAdapter(imageViews);
//        mViewPagerBanner.setAdapter(bannerPagerAdapter);

        //小点的数量会变,还需要再次初始化,先将以前的小点给清除掉
        mLlDot.removeAllViews();
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(-2,
                ViewGroup.LayoutParams.WRAP_CONTENT);
        params.setMargins(0, 0, 10, 0);
        for (int i = 0; i < mPicSize; i++) {
            ImageView imageView = new ImageView(getContext());
            //设置为小点一样的背景图
            imageView.setImageResource(R.drawable.bg_dot);
            //通过LayoutParams设置点之间的间距
            mLlDot.addView(imageView, params);
        }
//        selectDot(0);
//        mViewPagerBanner.setCurrentItem((Integer.MAX_VALUE / 2) - Integer.MAX_VALUE / 2 % mPicSize);
        //开始使用Handler发消息,发之前最好先移除一遍,免得先前的消息影响到最新的图片
        mHandler.removeCallbacksAndMessages(null);
        mHandler.sendEmptyMessageDelayed(0, 2000);
    }

    class MyPageChangeListener implements ViewPager.OnPageChangeListener {

        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

        }

        @Override
        public void onPageSelected(int position) {
            selectDot(position % mPicSize);
        }

        @Override
        public void onPageScrollStateChanged(int state) {

        }
    }
}
    //里面的viewpager的adapter
    public class BannerPagerAdapter extends PagerAdapter {

    private ArrayList<ImageView> mImageViews;
    public BannerPagerAdapter(ArrayList<ImageView> imageViews) {
        mImageViews = imageViews;
    }

    @Override
    public int getCount() {
        return Integer.MAX_VALUE;
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return view==object;
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        //为了保证数组不越界 通过%mImageViews.size来解决
        ImageView imageView = mImageViews.get(position%mImageViews.size());
        container.addView(imageView);
        return imageView;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        container.removeView((View) object);
    }

    //更新以前的数据
    public void updateData(ArrayList<ImageView> imageViews) {
        mImageViews.clear();
        mImageViews.addAll(imageViews);
        notifyDataSetChanged();
    }
}
//使用方法
mBannerView = new BannerView(getContext(),picUrls,titles);
listview自定义加载更多
        lv.setOnScrollListener(new AbsListView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
                //判断是否停下来滚动了
                if(scrollState== AbsListView.OnScrollListener.SCROLL_STATE_IDLE){
                    //是,判断最后一个条目
                    // 是否为可见的最后一个条目
                    int lastVisiblePosition = mLvHot.getLastVisiblePosition();
                    //获得最后一个条目在列表中的位置
                    int count = mLvHot.getAdapter().getCount();
                    Log.e(getClass().getSimpleName() + "xmg", "onScrollStateChanged: " + "lastVisiblePosition"+
                            lastVisiblePosition+" count:"+count);
                    if(lastVisiblePosition==count-1){
                        //认为可见了,需要请求更多数据
                        requestData(true);
                    }
                }
            }

            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {

            }
        });
ViewGroup触摸事件的拦截和处理
//用来复写触摸传递相关的方法,让X轴的滑动不再自己处理,能够往下发,发给子控件
public class MyPtrClassicFrameLayout extends PtrClassicFrameLayout {
    public MyPtrClassicFrameLayout(Context context) {
        super(context);
    }

    public MyPtrClassicFrameLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    //dispatchTouchEventSupper 代表正常的分发逻辑,可以让子控件接收到触摸事件
    //dispatchTouchEvent 会有下拉刷新的自行处理的效果,不会再把触摸事件往下发

        float mStartX = 0;
        float mStartY = 0;

    float mOffsetX = 0;//代表一次触摸过程中的x轴的总偏移量
    float mOffsetY = 0;//代表一次触摸过程中的y轴的总偏移量
    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        //直接返回时是这个super.dispatchTouchEvent(e);,就还是以前的下拉刷新逻辑,子控件是拿不到触摸事件的
        //判断是否为X 方向上的触摸移动
        int eventAction = event.getAction();
        switch (eventAction) {
            case MotionEvent.ACTION_DOWN:
                mStartX = event.getX();
                mStartY = event.getY();
                //每次down的时候都要总的偏移量置0,免得影响
                mOffsetX = 0;
                mOffsetY = 0;
                break;
            case MotionEvent.ACTION_MOVE:
                float newX = event.getX();
                float newY = event.getY();
                float dx = newX-mStartX;
                float dy = newY-mStartY;
                 TODO:
                mOffsetX+=dx;
                mOffsetY+=dy;
                if(Math.abs(mOffsetX)> ViewConfiguration.getTouchSlop()&&
                        Math.abs(mOffsetX)>Math.abs(mOffsetY)){
                    return dispatchTouchEventSupper(event);
                }
                mStartX = newX;
                mStartY = newY;
                break;
            case MotionEvent.ACTION_UP:
                break;
        }
            //是,就需要将触摸事件发给子控件,这个时候调用这个dispatchTouchEventSupper方法来进行返回,就能够让子控件拿到事件了
        return super.dispatchTouchEvent(event);
    }
}
xml保存数据:strings
//R.values.strings
<resources>
    <string name="app_name">NewsReader08</string>

    <array name="news_titles">
        <item>头条</item>
        <item>要闻</item>
        <item>娱乐</item>
        <item>体育</item>
        <item>网易号</item>
        <item>北京</item>
        <item>视频</item>
        <item>财经</item>
        <item>科技</item>
        <item>汽车</item>
        <item>时尚</item>
        <item>图片</item>
        <item>直播</item>
        <item>热点</item>
        <item>跟贴</item>
        <item>房产</item>
        <item>股票</item>
    </array>

    <array name="to_add_news_titles">
        <item>轻松一刻</item>
        <item>段子</item>
        <item>美女</item>
    </array>
</resources>
//代码中获取
String[] stringArray = getResources().getStringArray(R.array.news_titles);
List<String> strings = Arrays.asList(stringArray);
view的隐藏可见与animation的配合使用
           mTranslateAnimShow = new TranslateAnimation
               (TranslateAnimation.RELATIVE_TO_SELF, 0,
                TranslateAnimation.RELATIVE_TO_SELF, 0,
                TranslateAnimation.RELATIVE_TO_SELF, -1,
                TranslateAnimation.RELATIVE_TO_SELF, 0);
        mTranslateAnimShow.setDuration(1000);

                mFlChangeTitle.setVisibility(View.VISIBLE);
                mFlChangeTitle.startAnimation(mTranslateAnimShow);
FragmentTabHost的使用
        //1初始化 把它需要的参数,比如FragmentManager传过去
        mTabHost.setup(getApplicationContext(),getSupportFragmentManager(),R.id.fl_content);
        int[] resIds = new int[]{R.drawable.tab_news,R.drawable.tab_va,R.drawable.tab_topic,R.drawable.tab_my};
        String[] tabTexts = new String[]{"新闻","直播","话题","我的"};
        Class[] fragments = new Class[]{NewsFragment.class, VaFragment.class, TopicFragment.class, MeFragment.class};
        //2 接下来开始放入一个个Tab进来
        for (int i = 0; i < resIds.length; i++) {
            TabHost.TabSpec tabSpec = mTabHost.newTabSpec(String.valueOf(i));
            //需要指定这个Tab的长相
            View inflate = View.inflate(getApplicationContext(), R.layout.item_tab, null);
            ImageView ivTab = (ImageView) inflate.findViewById(R.id.iv_tab);
            TextView tvTab = (TextView) inflate.findViewById(R.id.tv_tab);
            ivTab.setImageResource(resIds[i]);
            tvTab.setText(tabTexts[i]);

            tabSpec.setIndicator(inflate);

            Class fragmentClazz = fragments[i];
            mTabHost.addTab(tabSpec,fragmentClazz ,null);
        }

        mTabHost.setOnTabChangedListener(new TabHost.OnTabChangeListener() {
            @Override
            public void onTabChanged(String tabId) {
                Toast.makeText(HomeActivity.this, "你切换到了:"+tabId, Toast.LENGTH_SHORT).show();
            }
        });
修改FragmentTabhost为 hide show
public class MyFragmentTabHost extends TabHost
        implements TabHost.OnTabChangeListener {
    private final ArrayList<TabInfo> mTabs = new ArrayList<>();

    private FrameLayout mRealTabContent;
    private Context mContext;
    private FragmentManager mFragmentManager;
    private int mContainerId;
    private OnTabChangeListener mOnTabChangeListener;
    private TabInfo mLastTab;
    private boolean mAttached;

    static final class TabInfo {
        final @NonNull String tag;
        final @NonNull Class<?> clss;
        final @Nullable Bundle args;
        Fragment fragment;

        TabInfo(@NonNull String _tag, @NonNull Class<?> _class, @Nullable Bundle _args) {
            tag = _tag;
            clss = _class;
            args = _args;
        }
    }

    static class DummyTabFactory implements TabContentFactory {
        private final Context mContext;

        public DummyTabFactory(Context context) {
            mContext = context;
        }

        @Override
        public View createTabContent(String tag) {
            View v = new View(mContext);
            v.setMinimumWidth(0);
            v.setMinimumHeight(0);
            return v;
        }
    }

    static class SavedState extends BaseSavedState {
        String curTab;

        SavedState(Parcelable superState) {
            super(superState);
        }

        SavedState(Parcel in) {
            super(in);
            curTab = in.readString();
        }

        @Override
        public void writeToParcel(Parcel out, int flags) {
            super.writeToParcel(out, flags);
            out.writeString(curTab);
        }

        @Override
        public String toString() {
            return "FragmentTabHost.SavedState{"
                    + Integer.toHexString(System.identityHashCode(this))
                    + " curTab=" + curTab + "}";
        }

        public static final Creator<SavedState> CREATOR
                = new Creator<SavedState>() {
            @Override
            public SavedState createFromParcel(Parcel in) {
                return new SavedState(in);
            }

            @Override
            public SavedState[] newArray(int size) {
                return new SavedState[size];
            }
        };
    }

    public MyFragmentTabHost(Context context) {
        // Note that we call through to the version that takes an AttributeSet,
        // because the simple Context construct can result in a broken object!
        super(context, null);
        initFragmentTabHost(context, null);
    }

    public MyFragmentTabHost(Context context, AttributeSet attrs) {
        super(context, attrs);
        initFragmentTabHost(context, attrs);
    }

    private void initFragmentTabHost(Context context, AttributeSet attrs) {
        final TypedArray a = context.obtainStyledAttributes(attrs,
                new int[] { android.R.attr.inflatedId }, 0, 0);
        mContainerId = a.getResourceId(0, 0);
        a.recycle();

        super.setOnTabChangedListener(this);
    }

    private void ensureHierarchy(Context context) {
        // If owner hasn't made its own view hierarchy, then as a convenience
        // we will construct a standard one here.
        if (findViewById(android.R.id.tabs) == null) {
            LinearLayout ll = new LinearLayout(context);
            ll.setOrientation(LinearLayout.VERTICAL);
            addView(ll, new LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT,
                    ViewGroup.LayoutParams.MATCH_PARENT));

            TabWidget tw = new TabWidget(context);
            tw.setId(android.R.id.tabs);
            tw.setOrientation(TabWidget.HORIZONTAL);
            ll.addView(tw, new LinearLayout.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT,
                    ViewGroup.LayoutParams.WRAP_CONTENT, 0));

            FrameLayout fl = new FrameLayout(context);
            fl.setId(android.R.id.tabcontent);
            ll.addView(fl, new LinearLayout.LayoutParams(0, 0, 0));

            mRealTabContent = fl = new FrameLayout(context);
            mRealTabContent.setId(mContainerId);
            ll.addView(fl, new LinearLayout.LayoutParams(
                    LinearLayout.LayoutParams.MATCH_PARENT, 0, 1));
        }
    }

    /**
     * @deprecated Don't call the original TabHost setup, you must instead
     * call {@link #setup(Context, FragmentManager)} or
     * {@link #setup(Context, FragmentManager, int)}.
     */
    @Override @Deprecated
    public void setup() {
        throw new IllegalStateException(
                "Must call setup() that takes a Context and FragmentManager");
    }

    public void setup(Context context, FragmentManager manager) {
        ensureHierarchy(context);  // Ensure views required by super.setup()
        super.setup();
        mContext = context;
        mFragmentManager = manager;
        ensureContent();
    }

    public void setup(Context context, FragmentManager manager, int containerId) {
        ensureHierarchy(context);  // Ensure views required by super.setup()
        super.setup();
        mContext = context;
        mFragmentManager = manager;
        mContainerId = containerId;
        ensureContent();
        mRealTabContent.setId(containerId);

        // We must have an ID to be able to save/restore our state.  If
        // the owner hasn't set one at this point, we will set it ourselves.
        if (getId() == View.NO_ID) {
            setId(android.R.id.tabhost);
        }
    }

    private void ensureContent() {
        if (mRealTabContent == null) {
            mRealTabContent = (FrameLayout)findViewById(mContainerId);
            if (mRealTabContent == null) {
                throw new IllegalStateException(
                        "No tab content FrameLayout found for id " + mContainerId);
            }
        }
    }

    @Override
    public void setOnTabChangedListener(OnTabChangeListener l) {
        mOnTabChangeListener = l;
    }

    public void addTab(@NonNull TabSpec tabSpec, @NonNull Class<?> clss,
            @Nullable Bundle args) {
        tabSpec.setContent(new DummyTabFactory(mContext));

        final String tag = tabSpec.getTag();
        final TabInfo info = new TabInfo(tag, clss, args);

        if (mAttached) {
            // If we are already attached to the window, then check to make
            // sure this tab's fragment is inactive if it exists.  This shouldn't
            // normally happen.
            info.fragment = mFragmentManager.findFragmentByTag(tag);
            if (info.fragment != null && !info.fragment.isDetached()) {
                final FragmentTransaction ft = mFragmentManager.beginTransaction();
                ft.detach(info.fragment);
                ft.commit();
            }
        }

        mTabs.add(info);
        addTab(tabSpec);
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();

        final String currentTag = getCurrentTabTag();

        // Go through all tabs and make sure their fragments match
        // the correct state.
        FragmentTransaction ft = null;
        for (int i = 0, count = mTabs.size(); i < count; i++) {
            final TabInfo tab = mTabs.get(i);
            tab.fragment = mFragmentManager.findFragmentByTag(tab.tag);
            if (tab.fragment != null && !tab.fragment.isDetached()) {
                if (tab.tag.equals(currentTag)) {
                    // The fragment for this tab is already there and
                    // active, and it is what we really want to have
                    // as the current tab.  Nothing to do.
                    mLastTab = tab;
                } else {
                    // This fragment was restored in the active state,
                    // but is not the current tab.  Deactivate it.
                    if (ft == null) {
                        ft = mFragmentManager.beginTransaction();
                    }
                    ft.detach(tab.fragment);
                }
            }
        }

        // We are now ready to go.  Make sure we are switched to the
        // correct tab.
        mAttached = true;
        ft = doTabChanged(currentTag, ft);
        if (ft != null) {
            ft.commit();
            mFragmentManager.executePendingTransactions();
        }
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        mAttached = false;
    }

    @Override
    protected Parcelable onSaveInstanceState() {
        Parcelable superState = super.onSaveInstanceState();
        SavedState ss = new SavedState(superState);
        ss.curTab = getCurrentTabTag();
        return ss;
    }

    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        if (!(state instanceof SavedState)) {
            super.onRestoreInstanceState(state);
            return;
        }
        SavedState ss = (SavedState) state;
        super.onRestoreInstanceState(ss.getSuperState());
        setCurrentTabByTag(ss.curTab);
    }

    @Override
    public void onTabChanged(String tabId) {
        if (mAttached) {
            final FragmentTransaction ft = doTabChanged(tabId, null);
            if (ft != null) {
                ft.commit();
            }
        }
        if (mOnTabChangeListener != null) {
            mOnTabChangeListener.onTabChanged(tabId);
        }
    }

    @Nullable
    private FragmentTransaction doTabChanged(@Nullable String tag,
            @Nullable FragmentTransaction ft) {
        final TabInfo newTab = getTabInfoForTag(tag);
        if (mLastTab != newTab) {
            if (ft == null) {
                ft = mFragmentManager.beginTransaction();
            }

            if (mLastTab != null) {
                if (mLastTab.fragment != null) {
//                    ft.detach(mLastTab.fragment);
                    ft.hide(mLastTab.fragment);
                }
            }

            if (newTab != null) {
                if (newTab.fragment == null) {
                    newTab.fragment = Fragment.instantiate(mContext,
                            newTab.clss.getName(), newTab.args);
                    ft.add(mContainerId, newTab.fragment, newTab.tag);
                } else {
//                    ft.attach(newTab.fragment);
                    if(newTab.fragment.isHidden()){
                        ft.show(newTab.fragment);
                    }
                }
            }

            mLastTab = newTab;
        }

        return ft;
    }

    @Nullable
    private TabInfo getTabInfoForTag(String tabId) {
        for (int i = 0, count = mTabs.size(); i < count; i++) {
            final TabInfo tab = mTabs.get(i);
            if (tab.tag.equals(tabId)) {
                return tab;
            }
        }
        return null;
    }
}
获取hashcode
public class HashUtil {
    public static String getHashCodeFileName(String picUrl){
        int hashCode = picUrl.hashCode();
        return hashCode+".jpg";
    }
}
使用缓存目录
File file = new File(getExternalCacheDir(), HashUtil.getHashCodeFileName(picUrl));
画笔测量文字大小
mMeasureTextWidth = mTextPaint.measureText(TEXT);
自定义控件的动画效果
        mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                mCurcentTime += 100;
                //判断一下,超过或等于总时间,就不要再发消息了
                if (mCurcentTime >= mTotalTime) {
                    //最后一次重绘,将会画出360度的圆弧
                    mCurcentTime = mTotalTime;
                    invalidate();
                    //关闭页面
                    if(mOnSkipListener!=null){
                        mOnSkipListener.onSkip();
                    }
                    Toast.makeText(getContext(), "我要关闭该页面了", Toast.LENGTH_SHORT).show();
                    return;
                }
                invalidate();
                mHandler.sendEmptyMessageDelayed(0, 100);
            }
        };
自定义广告页中的可以点击跳过的圆形view
public class SkipView extends View {

    public static final int ARC_WIDTH = 5;
    public static final int TEXT_PADDING = 8;
    public static final int TEXT_SIZE = 16;
    public static final String TEXT = "跳过";
    private Paint mOutArcPaint;
    private Paint mInnerCirclePaint;
    private Paint mTextPaint;
    private float mMeasureTextWidth;
    private float mInnerCircleDoubleRadius;
    private float mOutArcDoubleRadius;
    private RectF mRectF;
    private Handler mHandler;

    public SkipView(Context context) {
        super(context);
    }

    public SkipView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        //外部圆弧对应画笔
        mOutArcPaint = new Paint();
        mOutArcPaint.setColor(Color.RED);
        mOutArcPaint.setStrokeWidth(ARC_WIDTH);
        mOutArcPaint.setStyle(Paint.Style.STROKE);
        mOutArcPaint.setAntiAlias(true);
        //内部圆对应画笔
        mInnerCirclePaint = new Paint();
        mInnerCirclePaint.setAntiAlias(true);
        mInnerCirclePaint.setColor(Color.GRAY);
        //文字对应画笔
        mTextPaint = new Paint();
        mTextPaint.setAntiAlias(true);
        mTextPaint.setColor(Color.WHITE);
        mTextPaint.setTextSize(TEXT_SIZE);

        //计算圆的半径大小
        //先计算出文字的宽度
        mMeasureTextWidth = mTextPaint.measureText(TEXT);

        //内部圆直径=文字的宽度+2PADDING
        mInnerCircleDoubleRadius = mMeasureTextWidth + 2 * TEXT_PADDING;
        //外部圆直径=内部圆直径+2ARC_WIDTH
        mOutArcDoubleRadius = mInnerCircleDoubleRadius + 2 * ARC_WIDTH;

        //准备一个外部圆弧绘制所需要的RectF对象
        mRectF = new RectF(0 + ARC_WIDTH / 2, 0 + ARC_WIDTH / 2,
                mOutArcDoubleRadius - ARC_WIDTH / 2, mOutArcDoubleRadius - ARC_WIDTH / 2);
        mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                mCurcentTime += 100;
                //判断一下,超过或等于总时间,就不要再发消息了
                if (mCurcentTime >= mTotalTime) {
                    //最后一次重绘,将会画出360度的圆弧
                    mCurcentTime = mTotalTime;
                    invalidate();
                    //关闭页面
                    if(mOnSkipListener!=null){
                        mOnSkipListener.onSkip();
                    }
                    Toast.makeText(getContext(), "我要关闭该页面了", Toast.LENGTH_SHORT).show();
                    return;
                }
                invalidate();
                mHandler.sendEmptyMessageDelayed(0, 100);
            }
        };
    }

    //触摸事件监听
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                setAlpha(0.5f);
                break;
            case MotionEvent.ACTION_UP:
                setAlpha(1f);
                //不要再让Handler一直发消息去重绘了
                mHandler.removeCallbacksAndMessages(null);
                Toast.makeText(getContext(), "关闭该页面", Toast.LENGTH_SHORT).show();
                if(mOnSkipListener!=null){
                    mOnSkipListener.onSkip();
                }
                break;
            default:
                break;
        }

        return true;
    }

    public void start() {
        start(3000);
    }

    public void start(int totalTime) {
        mTotalTime = totalTime*10;
        mHandler.sendEmptyMessageDelayed(0, 100);
    }

    //测量
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        switch (widthMode) {
            case MeasureSpec.EXACTLY:
            case MeasureSpec.AT_MOST:
                widthSize = Math.min(widthSize, (int) mOutArcDoubleRadius);
                break;
        }
        switch (heightMode) {
            case MeasureSpec.EXACTLY:
            case MeasureSpec.AT_MOST:
                heightSize = Math.min(heightSize, (int) mOutArcDoubleRadius);
                break;
        }
        int min = Math.min(widthSize, heightSize);
        //核心方法
        setMeasuredDimension(min, min);
    }

    //draw绘制
    private int mCurcentTime = 0;//当前时间
    private int mTotalTime = 3000;//总的时间

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int measuredHeight = getMeasuredHeight();
        int measuredWidth = getMeasuredWidth();
        //绘制3个
        canvas.save();
        //圆的开始角度为三点钟方向
        canvas.rotate(-90, measuredWidth / 2, measuredHeight / 2);
        float percent = mCurcentTime * 1f / mTotalTime;
        canvas.drawArc(mRectF, 0, percent * 360, false, mOutArcPaint);
        canvas.restore();

        canvas.drawCircle(measuredWidth / 2, measuredHeight / 2, mInnerCircleDoubleRadius / 2, mInnerCirclePaint);
         通过FontMetrics中的属性来修改Y轴位置
        Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();
        float ascent = fontMetrics.ascent;
        float descent = fontMetrics.descent;
        float top = fontMetrics.top;
        float bottom = fontMetrics.bottom;
        //公式: baseLine = 你想让控件的中间位于的Y上的坐标位置-(top+Bottom)/2;
        int baseLine = (int) (measuredHeight / 2 - (top + bottom) / 2);
        canvas.drawText(TEXT, measuredWidth / 2 - mMeasureTextWidth / 2, baseLine, mTextPaint);
    }

    //监听事件
    private OnSkipListener mOnSkipListener;
    public void setOnSkipListener(OnSkipListener onSkipListener){
        mOnSkipListener = onSkipListener;
    }
    public interface  OnSkipListener{
        void onSkip();
    }
}
后台下载图片的IntentService
public class DownloadPicService extends IntentService {

    public static final String DOWNLOAD_PIC = "download_pic";

    public DownloadPicService() {
        super("DownloadPicService");
    }

    //该方法中的代码直接执行耗时间的任务,而且任务执行完毕后,会自杀
    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        //开始去拿图片地址进行下载
        AdsBean adsBean = (AdsBean) intent.getSerializableExtra(DOWNLOAD_PIC);
        //下载图片
        ArrayList<AdItemBean> ads = adsBean.getAds();

        //1 创建一个OKHttpClient
        OkHttpClient okHttpClient = new OkHttpClient();
        //2 创建一个Request请求,设置Url
        //3 newCall方法,可以去生成Call对象
        //4 使用Call对象的enqueue/execute方法来执行网络请求
        for (int i = 0; i < ads.size(); i++) {
            AdItemBean adItemBean = ads.get(i);
            //取出图片地址
            final String picUrl = adItemBean.getRes_url()[0];
            //如果本地有这张图片,就别再下载了
            File file = new File(getExternalCacheDir(), HashUtil.getHashCodeFileName(picUrl));
            if(file.exists()&&file.length()>0){
                Log.e(getClass().getSimpleName() + "xmg", "onHandleIntent: " + "该图片已经存在,不再下载");
                //结束这一次的循环,开始下一个循环
                continue;
            }

            Request request = new Request.Builder().url(picUrl).build();
            okHttpClient.newCall(request).enqueue(new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {
                    e.printStackTrace();
                }

                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    if(!response.isSuccessful()){
                        onFailure(call,new IOException("响应失败"));
                    }
                    //把请求到的数据转为图片
                    InputStream inputStream = response.body().byteStream();
                    Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
                    //将bitmap保存到手机存储盘,免的每次都去下载图片
                    //getExternalCacheDir() 获得SD卡的缓存目录 mnt/sdcard/android/data/包名/caches
                    //getCacheDir() data/data/包名/cache
                    //图片的下载地址作为文件名,但是文件名里不能有非法字符,这里将图片的下载地址字符串的hashCode来作为文件名
                    File file = new File(getExternalCacheDir(), HashUtil.getHashCodeFileName(picUrl));
                    FileOutputStream fileOutputStream = new FileOutputStream(file);
                    bitmap.compress(Bitmap.CompressFormat.JPEG,100,fileOutputStream);
                }
            });
        }
    }
}

KingTv

高仿全民直播(全民TV),项目采用 MVP + RXJava + Retrofit + OKHttp + Material Design + Dagger2 + Base + Glide + GreenDao构建。
https://github.com/jenly1314/KingTV.git
自定义不缓存任何Fragment的FragmentPagerAdpater
public abstract class CustomFragmentPagerAdapter extends PagerAdapter {

    private static final String TAG = CustomFragmentPagerAdapter.class.getSimpleName();

    private final FragmentManager mFragmentManager;
    private FragmentTransaction mCurTransaction;
    private Fragment mCurrentPrimaryItem;

    public CustomFragmentPagerAdapter(FragmentManager fm) {
        mFragmentManager = fm;
    }

    public abstract Fragment getItem(int position);

    @Override
    public void startUpdate(ViewGroup container) {
        if (container.getId() == View.NO_ID) {
            throw new IllegalStateException("ViewPager with adapter " + this + " requires a view id");
        }
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }

        Fragment fragment = getItem(position);
        Log.i(TAG, "Adding fragment item #" + position + ": f=" + fragment);
        fragment.setMenuVisibility(false);
        fragment.setUserVisibleHint(false);
        mCurTransaction.add(container.getId(), fragment, makeFragmentName(container.getId(), getItemId(position)));
        return fragment;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        Fragment fragment = (Fragment) object;

        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }
        Log.i(TAG, "Removing fragment #" + position + ": f=" + fragment + " v=" + fragment.getView());
        mCurTransaction.remove(fragment);
    }

    @Override
    public void setPrimaryItem(ViewGroup container, int position, Object object) {
        Fragment fragment = (Fragment) object;
        if (fragment != mCurrentPrimaryItem) {
            if (mCurrentPrimaryItem != null) {
                mCurrentPrimaryItem.setMenuVisibility(false);
                mCurrentPrimaryItem.setUserVisibleHint(false);
            }
            if (fragment != null) {
                fragment.setMenuVisibility(true);
                fragment.setUserVisibleHint(true);
            }
            mCurrentPrimaryItem = fragment;
        }
    }

    public void commitUpdate() {
        if (mCurTransaction != null) {
            mCurTransaction.commitNowAllowingStateLoss();
            mCurTransaction = null;
        }
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return ((Fragment) object).getView() == view;
    }

    protected String makeFragmentName(int viewId, long id) {
        return "android:switcher:" + viewId + ":" + id;
    }

    protected long getItemId(int position) {
        return position;
    }
}
public class MainActivity extends AppCompatActivity {

    ViewPager mViewPager;
    List<String> mListContent = new ArrayList<>();

    Fragment mCurrentFragment;
    CustomFragmentPagerAdapter mAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        for (int i = 0; i < 30; i++) {
            mListContent.add(String.valueOf(i));
        }

        mViewPager = (ViewPager) findViewById(R.id.id_view_pager);
        mViewPager.setAdapter(mAdapter = new CustomFragmentPagerAdapter(getSupportFragmentManager()) {
            @Override
            public int getCount() {
                return mListContent.size();
            }

            @Override
            public Fragment getItem(int position) {
                CustomFragment fragment = new CustomFragment();
                Bundle bundle = new Bundle();
                bundle.putString(CustomFragment.BUNDLE_KEY, "" + position);
                fragment.setArguments(bundle);
                return fragment;
            }

            @Override
            public void setPrimaryItem(ViewGroup container, int position, Object object) {
                super.setPrimaryItem(container, position, object);
                if (mCurrentFragment == null) {
                    commitUpdate();
                }
                mCurrentFragment = (Fragment) object;
            }

            @Override
            public int getItemPosition(Object object) {
                return PagerAdapter.POSITION_NONE;
            }
        });

        mViewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
            @Override
            public void onPageScrollStateChanged(int state) {
                super.onPageScrollStateChanged(state);
                //在viewPager状态停止时,我们才提交transcription
                if (state == ViewPager.SCROLL_STATE_IDLE) {  
                    mAdapter.commitUpdate();
                }
            }
        });
    }
}
处理屏幕的旋转
    public void clickFullScreen(){
        if(isLandscape()){
            getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        }else{
            getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        }
    }
        <activity android:name=".MainActivity"
            android:configChanges="keyboardHidden|orientation|screenSize"
            android:windowSoftInputMode="stateHidden|stateUnchanged" />
    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);

        if(isLandscape()){
            llRoomChat.setVisibility(View.GONE);
            ivFullScreen.setVisibility(View.GONE);
        }else{
            llRoomChat.setVisibility(View.VISIBLE);
            ivFullScreen.setVisibility(View.VISIBLE);
        }
        updateVideoLayoutParams();
    }
TabLayout
public class ViewPagerFragmentAdapter extends FragmentPagerAdapter{
    private List<Fragment> listData;
    private List<CharSequence> listTitle;
    public ViewPagerFragmentAdapter(FragmentManager fm, List<Fragment> listData){
        this(fm,listData,null);
    }
    public ViewPagerFragmentAdapter(FragmentManager fm, List<Fragment> listData, List<CharSequence> listTitle) {
        super(fm);
        this.listData = listData;
        this.listTitle = listTitle;
    }
    public List<Fragment> getListData() {
        return listData;
    }
    public void setListData(List<Fragment> listData) {
        this.listData = listData;
    }
    public List<CharSequence> getListTitle() {
        return listTitle;
    }
    public void setListTitle(List<CharSequence> listTitle) {
        this.listTitle = listTitle;
    }
    @Override
    public Fragment getItem(int position) {
        return listData==null ? null : listData.get(position) ;
    }
    @Override
    public int getCount() {
        return listData == null ? 0 : listData.size();
    }
    //实现该方法
    @Override
    public CharSequence getPageTitle(int position) {
        if(listTitle!=null && listTitle.size()!=0){
            return listTitle.get(position);
        }
        return super.getPageTitle(position);
    }
}
viewPagerFragmentAdapter = new ViewPagerFragmentAdapter(getFragmentManager(), listFragment, listTitles);
tabLayout.setupWithViewPager(viewPager);
//通过该该方法来来监听pager的选择事件
tabLayout.addOnTabSelectedListener();
保持屏幕常亮
Activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
EasyRecyclerView自适应高度
   <com.jude.easyrecyclerview.EasyRecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        />
   item布局中
   把网格布局item中的父控件和imageview全部设置设置成了match_parent,而更换成一个固定值后,成功了!

   //解决item中图片显示不全的问题
       @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = mInflater.inflate(R.layout.item_grid, parent, false);
        //动态设置ImageView的宽高,根据自己每行item数量计算
        //dm.widthPixels-dip2px(20)即屏幕宽度-左右10dp+10dp=20dp再转换为px的宽度,最后/3得到每个item的宽高
        RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams((dm.widthPixels - dip2px(20)) / 3, (dm.widthPixels - dip2px(20)) / 3);
        view.setLayoutParams(lp);
        return new MyViewHolder(view);
    }

    //跳到其他页面,再返回过来,发现它会自动滑动到顶部
    //主要的原因是因为 RecyclerView抢了焦点 在RecyclerView的 
    父布局加上 android:focusable="true"; android:focusableInTouchMode="true";   
抓取Bugly捕获的信息
CrashReport.postCatchedException(e);
    public interface CrashHandler{
        void uncaughtException(Thread t,Throwable e);
    }
        mCrashHandler = crashHandler;
        new Handler(Looper.getMainLooper()).post(new Runnable() {
            @Override
            public void run() {
                for (;;) {
                    try {
                        Looper.loop();
                    } catch (Throwable e) {
                        if (mCrashHandler != null) {//捕获异常处理
                            mCrashHandler.uncaughtException(Looper.getMainLooper().getThread(), e);
                        }
                    }
                }
            }
        });
       Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                if(mCrashHandler!=null){//捕获异常处理
                    mCrashHandler.uncaughtException(t,e);
                }
            }
        });
线程池的封装
public class ThreadPoolManager {
    //单例模式
    private static ThreadPoolProxy mInstance;
    public static ThreadPoolProxy getInstance() {
        if (mInstance == null) {
            synchronized (ThreadPoolManager.class) {
                if (mInstance == null) {
                    //默认核心线程为2,非核心线程为5
                    mInstance = new ThreadPoolProxy(2, 5);
                }
            }
        }
        return mInstance;
    }
    //代理模式
    public static class ThreadPoolProxy {

        private ThreadPoolExecutor mThreadPoolExecutor;

        public ThreadPoolProxy(int corePoolSize, int maximumPoolSize) {
            initThreadPoolExecutor(corePoolSize, maximumPoolSize);
        }

        private void initThreadPoolExecutor(int corePoolSize, int maximumPoolSize) {
            if (mThreadPoolExecutor == null) {
                //阻塞缓冲队列
                BlockingDeque<Runnable> workQueue = new LinkedBlockingDeque<>();
                ThreadFactory threadFactory = Executors.defaultThreadFactory();
                //拒绝任务处理策略(抛弃旧的任务)
                RejectedExecutionHandler handler = new ThreadPoolExecutor.DiscardPolicy();
                mThreadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, 0, TimeUnit.MICROSECONDS, workQueue, threadFactory, handler);
            }
        }
        //提交
        public void submit(Runnable task) {
            if (mThreadPoolExecutor != null) {
                mThreadPoolExecutor.submit(task);
            }
        }
        //执行
        public void execute(Runnable task) {
            if (mThreadPoolExecutor != null) {
                mThreadPoolExecutor.execute(task);
            }
        }
        //移除
        public void remove(Runnable task) {
            if (mThreadPoolExecutor != null) {
                mThreadPoolExecutor.remove(task);
            }
        }
    }
}
RadioButton作为导航栏时,Fragment的处理
    <RadioGroup
        android:orientation="horizontal"
        android:padding="6dp"
        android:checkedButton="@+id/rbHome">
        <RadioButton
            android:id="@+id/rbHome"
            android:button="@null"
            android:drawablePadding="4dp"
            android:drawableTop="@drawable/btn_tabbar_wode_selector"/>
        <RadioButton
            android:id="@+id/rbLive"
            android:button="@null"
            android:drawablePadding="4dp"
            android:drawableTop="@drawable/btn_tabbar_wode_selector"/>
        <RadioButton
            android:id="@+id/rbFollw"
            android:button="@null"
            android:drawablePadding="4dp"
            android:drawableTop="@drawable/btn_tabbar_wode_selector">
        <RadioButton
            android:id="@+id/rbMe"
            android:button="@null"
            android:drawablePadding="4dp"
            android:drawableTop="@drawable/btn_tabbar_wode_selector"/>
    </RadioGroup>
    @OnClick({com.king.tv.R.id.rbHome, com.king.tv.R.id.rbLive, com.king.tv.R.id.rbFollw, com.king.tv.R.id.rbMe})
    public void onClick(View view) {
        switch (view.getId()) {
            case com.king.tv.R.id.rbHome:
                showHomeFragment();
                break;
            case com.king.tv.R.id.rbLive:
                showLiveFragment();
                break;
            case com.king.tv.R.id.rbFollw:
                showFollowFragment();
                break;
            case com.king.tv.R.id.rbMe:
                showMineFragment();
                break;
        }
    }
    //只展示了Home的,其他同理
    public void showHomeFragment(){
    //开启事务
        FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
        //隐藏所有Fragment
        hideAllFragment(fragmentTransaction);
        if(homeFragment == null){
            homeFragment = HomeFragment.newInstance();
            fragmentTransaction.add(com.king.tv.R.id.fragmentContent,homeFragment);
        }
        commitShowFragment(fragmentTransaction,homeFragment);
    }
    //隐藏所有Fragment
    public void hideAllFragment(FragmentTransaction fragmentTransaction){
        hideFragment(fragmentTransaction,homeFragment);
        hideFragment(fragmentTransaction,liveFragment);
        hideFragment(fragmentTransaction,followFragment);
        hideFragment(fragmentTransaction,mineFragment);
    }
    //隐藏单个的Fragment
    private void hideFragment(FragmentTransaction fragmentTransaction,Fragment fragment){
        if(fragment!=null){
            fragmentTransaction.hide(fragment);
        }
    }
    //展示需要展示的Fragment
    public void commitShowFragment(FragmentTransaction fragmentTransaction,Fragment fragment){
        fragmentTransaction.show(fragment);
        fragmentTransaction.commit();
    }        

KingsGirls

项目采用RXJava + Retrofit + OKHttp + Material Design + Base + Glide构建,数据来自gank.io。是一款以瀑布流的形式展示美女福利的App
https://github.com/jenly1314/KingsGirls.git
半透明状态栏
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            Window window = getWindow(); 
            window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
            ,WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        }
半透明虚拟按键
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            Window window = getWindow();
            window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION
            ,WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
        }
Glide缓存策略
        Glide.with(context)
                .load(url).error(R.drawable.ic_welcome).crossFade()
                //缓存策略,只保存源文件到本地
                .diskCacheStrategy(DiskCacheStrategy.SOURCE)
                .into(iv);
获取屏幕的DecorView ContentView
//DecorView
ViewGroup view = (ViewGroup)activity.getWindow().getDecorView();
//ContentView
FrameLayout content = (FrameLayout)view.findViewById(android.R.id.content);
//activity的根布局
content.getChildAt(0);
对findViewById进行抽取
//利用泛型进行转换,转换成View的实现类
   protected <T extends View> T findView(int resId){
        return (T)findViewById(resId);
    }
//demo
TextView=findView(R.id.tv);
安装apk文件
    //路径
    public static void installApk(Context context,String path){
        installApk(context,new File(path));
    }
    //文件
    public static void installApk(Context context,File file){

        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        Uri uriData = Uri.fromFile(file);
        String type = "application/vnd.android.package-archive";

        intent.setDataAndType(uriData, type);
        context.startActivity(intent);
    }
权限相关
    //检测权限
    public static boolean checkSelfPermission(Context context, @NonNull String permission){
        return ContextCompat.checkSelfPermission(context, permission)
         == PackageManager.PERMISSION_GRANTED;
    }
    //申请权限
    public static void requestPermission(Activity activity, @NonNull String permission, int requestCode){
        ActivityCompat.requestPermissions(activity,new String[]{permission}, requestCode);
    }
    //显示请求权限界面
    public static void shouldShowRequestPermissionRationale(Activity activity, @NonNull String permission){
        ActivityCompat.shouldShowRequestPermissionRationale(activity,permission);
    }
Coordinator使用相关
            <android.support.design.widget.AppBarLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:fitsSystemWindows="true"
                android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

                <android.support.v7.widget.Toolbar
                    android:id="@+id/toolBar"
                    android:layout_width="match_parent"
                    android:layout_height="?attr/actionBarSize"
                    android:background="@color/colorPrimary"
                    android:title="@string/app_name"
                    app:layout_collapseMode="pin"
                    //被上拉隐藏
                    app:layout_scrollFlags="scroll|enterAlways"
                    app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
                    app:titleTextColor="@color/white"/>
                <android.support.design.widget.TabLayout
                    //没有设置app:layout_scrollFlags,不会被上拉隐藏
                    android:id="@+id/tabLayout"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:background="@color/white"
                    app:tabIndicatorColor="@color/red"
                    app:tabIndicatorHeight="3dp"
                    app:tabMinWidth="60dp"
                    app:tabMode="scrollable"
                    app:tabSelectedTextColor="@color/red"
                    app:tabTextAppearance="@style/TabLayoutTextAppearance"
                    app:tabTextColor="@color/black"/>
            </android.support.design.widget.AppBarLayout>
在style中去除actionbar&自定义lv分割线样式
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    //去除actionbar
        <item name="windowActionBar">false</item>
        <item name="windowNoTitle">true</item>
    //自定义listview分割线样式        
        <item name="android:listDivider">@drawable/list_divider</item>
    </style>
Fragment的工厂方法,从而实现在Fragment创建时输入一些数据
    public static GirlsFragment newInstance(LayoutType layoutType) {
        Bundle args = new Bundle();
        GirlsFragment fragment = new GirlsFragment();
        fragment.setArguments(args);
        fragment.layoutType = layoutType;
        return fragment;
    }
Recyclerview瀑布流时第一行有空白的问题的解决
 StaggeredGridLayoutManager staggeredGridLayoutManager = new StaggeredGridLayoutManager(2,StaggeredGridLayoutManager.VERTICAL);
                staggeredGridLayoutManager.setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_NONE);
recyclerView.setLayoutManager(staggeredGridLayoutManager);
recyclerView.getRecyclerView().addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
      super.onScrollStateChanged(recyclerView, newState);
      //防止第一行到顶部有空白区域
      staggeredGridLayoutManager.invalidateSpanAssignments();
       }
});
从被点击的view以缩放图的形式打开一个act的过渡动画
ActivityOptionsCompat activityOptionsCompat =ActivityOptionsCompat.makeScaleUpAnimation(source
,source.getWidth()/2,source.getHeight()/2,0,0);
ActivityCompat.startActivity(getActivity(),intent,activityOptionsCompat.toBundle());
Retrofit模版
    //Rxjava
    compile 'io.reactivex:rxandroid:1.2.1'
    compile 'io.reactivex:rxjava:1.1.6'
    //retrofit
    compile 'com.squareup.retrofit2:retrofit:2.1.0'
    compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
    compile 'com.squareup.retrofit2:converter-gson:2.1.0'
public class APIRetrofit {
    private static Retrofit mInstance;
    private static OkHttpClient mOKHttpClient;

    public static Retrofit getInstance() {
        if (mInstance == null) {
            synchronized (APIRetrofit.class) {
                if (mInstance == null) {
                    mInstance = new Retrofit.Builder()
                            .baseUrl("http://gank.io")
                            .addConverterFactory(GsonConverterFactory.create())
                            .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                            .client(getOkHttpClient())
                            .build();
                }
            }
        }
        return mInstance;
    }

    private static OkHttpClient getOkHttpClient() {
        if (mInstance == null) {
            synchronized (APIRetrofit.class) {
                if (mOKHttpClient == null) {
                    mOKHttpClient = new OkHttpClient.Builder()
                            .connectTimeout(10, TimeUnit.SECONDS)
                            .readTimeout(10, TimeUnit.SECONDS)
                            .writeTimeout(10, TimeUnit.SECONDS)
                            .build();
                }
            }
        }
        return mOKHttpClient;
    }
}
public interface APIService {
    /**
     * @param type 可选参数: Android | iOS | 休息视频 | 福利 | 拓展资源 | 前端 | 瞎推荐 | App
     */
    @GET("api/data/{type}/{count}/{page}")
    Observable<GirlResult> getGirs(@Path("type") String type, @Path("count") int count, @Path("page") int page);
}
APIRetrofit.getInstance()
                .create(APIService.class)
                .getGirs("福利", 1, n)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<GirlResult>() {
                    @Override
                    public void onCompleted() {
                        Logger.d("onCompleted");
                    }

                    @Override
                    public void onError(Throwable e) {
                        Logger.d("onError");
                    }

                    @Override
                    public void onNext(GirlResult girlResult) {
                        Logger.d(girlResult);
                        if (girlResult.isError()) {
                            mImageView.setImageResource(R.mipmap.ic_launcher_round);
                        }else{
                            List<GirlResult.Girl> results = girlResult.getResults();
                            GirlResult.Girl girl = results.get(0);
                            String url = girl.getUrl();
                            Picasso.with(MainActivity.this)
                                    .load(url)
                                    .into(mImageView);
                        }
                    }
                });
TabLayout
               <android.support.design.widget.TabLayout
                    android:id="@+id/tabLayout"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:background="@color/white"
                    app:tabIndicatorColor="@color/red"
                    app:tabIndicatorHeight="3dp"
                    app:tabMinWidth="60dp"
                    app:tabMode="scrollable"
                    app:tabSelectedTextColor="@color/red"
                    app:tabTextAppearance="@style/TabLayoutTextAppearance"
                    app:tabTextColor="@color/black"/>
    <style name = "TabLayoutTextAppearance"  parent="TextAppearance.AppCompat.Widget.ActionBar.Title">
        <item name="android:textSize">16sp</item>
        <item name="android:textAllCaps">false</item>
    </style>
//初始化
tabLayout.setupWithViewPager(viewPager);
dp sp px转换工具类
public class DensityUtil {
    private DensityUtil(){
        throw new AssertionError();
    }
    public static int dp2px(Context context,float dpVal){
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,dpVal,getDisplayMetrics(context));
    }
    public static int sp2px(Context context,float spVal){
        return (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,spVal,getDisplayMetrics(context));
    }
    public static int px2dp(Context context,float pxVal){
        return (int) (pxVal / getDisplayMetrics(context).density + 0.5f);
    }
    public static int px2sp(Context context,float pxVal){
        return (int) (pxVal / getDisplayMetrics(context).scaledDensity + 0.5f);
    }
    public static DisplayMetrics getDisplayMetrics(Context context){
        return context.getResources().getDisplayMetrics();
    }
}

重构

提示等待的进度条的抽取
public class BaseProgressDialog extends Dialog{

    //工厂模式
    public static BaseProgressDialog newInstance(Context context){
        return new BaseProgressDialog(context);
    }

    //三个构造器
    public BaseProgressDialog(Context context) {
        this(context,R.style.progress_dialog);
        initUI();
    }
    public BaseProgressDialog(Context context, int themeResId) {
        super(context, themeResId);
        initUI();
    }
    protected BaseProgressDialog(Context context, boolean cancelable, OnCancelListener cancelListener) {
        super(context, cancelable, cancelListener);
        initUI();
    }

    //初始化ui
    private void initUI(){
        setContentView(new ProgressBar(getContext()));
        getWindow().getAttributes().gravity = Gravity.CENTER;
        setCanceledOnTouchOutside(false);
    }
}
BaseAcitivity
public interface BaseInterface {
    public static final int NONE = -1;
    public static final int REQUEST_CODE = 0X01;
    public static final int RESULT_OK = Activity.RESULT_OK;
    public static final int RESULT_CANCELED = Activity.RESULT_CANCELED;
    public static final String KEY_ID = "key_id";
    public static final String KEY_TITLE = "key_title";
    public static final String KEY_URL = "key_url";
    public static final String KEY_SERIALIZABLE = "key_serializable";
    public static final String KEY_OBJECT = "key_object";
    public static final String KEY_FRAGMENT = "key_fragment";
    public abstract void initUI();
    public abstract void initData();
    public abstract void addListeners();
}
public abstract class BaseActivity extends AppCompatActivity implements  BaseInterface{
    //context
    protected Context context = this;

    //dialog一个progressdialog
    private Dialog dialog;
    private BaseProgressDialog progressDialog;

    //是否stop的标志位
    protected boolean isStop;
    //一个保存当前页面的变量
    protected int curPage;

    //将oncreate细分为了initui initdata  addlisteners三个部分
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        curPage = 1;
        initUI();
        initData();
        addListeners();
    }
    //生命周期相关
    @Override
    protected void onResume() {
        super.onResume();
        isStop = false;
    }

    @Override
    protected void onStop() {
        super.onStop();
        isStop = true;
        dismissProgressDialog();
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        dismissDialog();
    }
    //ContentView相关
    public static View getContentView(Activity activity){
        ViewGroup view = (ViewGroup)activity.getWindow().getDecorView();
        FrameLayout content = (FrameLayout)view.findViewById(android.R.id.content);
        return content.getChildAt(0);
    }
    protected View inflate(@LayoutRes int id){
        return inflate(id,null);
    }
    protected View inflate(@LayoutRes int id, @Nullable ViewGroup root){
       return LayoutInflater.from(context).inflate(id,root);
    }
    protected <T extends View> T findView(int resId){
        return (T)findViewById(resId);
    }
    //设置某一个view的监听事件
    protected void setOnClickListener(@IdRes int id,View.OnClickListener onClicklistener){
        findViewById(id).setOnClickListener(onClicklistener);
    }

    //intent相关
    protected Intent getIntent(Class<?> cls){
        return new Intent(context,cls);
    }
    protected Intent getIntent(Class<?> cls,int flags){
        Intent intent = getIntent(cls);
        intent.setFlags(flags);
        return intent;
    }

    //startAcitivity相关
    protected void startActivity(Class<?> cls){
        startActivity(getIntent(cls));
    }
    protected void startActivity(Class<?> cls,int flags){
        startActivity(getIntent(cls,flags));
    }
    protected void startActivityFinish(Class<?> cls){
        startActivity(cls);
        finish();
    }
    protected void startActivityFinish(Class<?> cls,int flags){
        startActivity(cls,flags);
        finish();
    }

    //Fragment相关
    public void replaceFragment(@IdRes int resId,Fragment fragment){
        replaceFragment(resId,fragment,false);
    }
    public void replaceFragment(@IdRes int resId, Fragment fragment, boolean isBackStack) {
        FragmentTransaction fragmentTransaction =  getSupportFragmentManager().beginTransaction();
        fragmentTransaction.replace(resId, fragment);
        if(isBackStack){
            fragmentTransaction.addToBackStack(null);
        }
        fragmentTransaction.commit();
    }

    //吐司相关
    protected void showToast(@StringRes  int resId){
        if(resId != NONE)
            ToastUtils.showToast(context,resId);
    }
    protected void showLongToast(@StringRes  int resId){
        ToastUtils.showToast(context,resId, Toast.LENGTH_LONG);
    }
    protected void showToast(CharSequence text){
        ToastUtils.showToast(context,text);
    }
    protected void showLongToast(CharSequence text){
        ToastUtils.showToast(context,text, Toast.LENGTH_LONG);
    }

    //文本检查
    public boolean checkInput(TextView tv){
        return checkInput(tv,NONE);
    }
    public boolean checkInput(TextView tv,@StringRes int resId){
        return checkInput(tv,resId,false);
    }
    public boolean checkInput(TextView tv,@StringRes int resId,boolean isShake){
        if(StringUtils.isBlank(tv.getText())){
            if(isShake)
                startShake(tv,resId);
            else
                showToast(resId);
            return false;
        }
        return true;
    }
    //动画相关
    public void startShake(View v,@StringRes int resId){
        startShake(v);
        showToast(resId);
    }
    public void startShake(View view){
        view.startAnimation(AnimationUtils.loadAnimation(context,R.anim.shake));
    }

    //软键盘相关
    public void hideInputMethod(EditText v) {
        SystemUtils.hideInputMethod(context,v);
    }
    public void showInputMethod(EditText v) {
        SystemUtils.showInputMethod(context,v);
    }


    //progressDialog相关
    public Dialog getProgressDialog() {
        return progressDialog;
    }
    protected void showProgressDialog(){
        showProgressDialog(new ProgressBar(context));
    }
    protected void showProgressDialog(@LayoutRes int resId){
        showProgressDialog(LayoutInflater.from(context).inflate(resId,null));
    }
    protected void showProgressDialog(View v){
        dismissProgressDialog();
        progressDialog = BaseProgressDialog.newInstance(context);
        progressDialog.setContentView(v);
        progressDialog.show();
    }
    protected void dismissProgressDialog(){
        dismissDialog(progressDialog);
    }

    //Dialog相关
    public Dialog getDialog() {
        return dialog;
    }
    protected void showDialog(View contentView){
        showDialog(context,contentView);
    }

    protected void showDialog(Context context,View contentView){
        dismissDialog();
        dialog = new Dialog(context,R.style.dialog);
        dialog.setContentView(contentView);
        dialog.setCanceledOnTouchOutside(false);
        getDialogWindow(dialog);
        dialog.show();
    }
    protected void dismissDialog(){
        dismissDialog(dialog);
    }
    protected void dismissDialog(Dialog dialog){
        if(dialog != null && dialog.isShowing()){
            dialog.dismiss();
        }
    }
    protected void getDialogWindow(Dialog dialog){
        Window window = dialog.getWindow();
        WindowManager.LayoutParams lp = window.getAttributes();
        lp.width = (int)(getWidthPixels()*0.9f);
        lp.gravity= Gravity.CENTER;
        window.setAttributes(lp);
    }

    //FragmentDialog相关
    protected void dismissDialogFragment(DialogFragment dialogFragment){
        if(dialogFragment != null){
            dialogFragment.dismiss();
        }
    }
    public void showDialogFragment(DialogFragment dialogFragment){
        String tag = dialogFragment.getTag() !=null ? dialogFragment.getTag() : dialogFragment.getClass().getSimpleName();
        showDialogFragment(dialogFragment,tag);
    }
    public void showDialogFragment(DialogFragment dialogFragment,String tag) {
        dialogFragment.show(getSupportFragmentManager(),tag);
    }

    //开始一个异步任务
    protected void asyncThread(Runnable runnable){
        new Thread(runnable).start();
    }


    //获取屏幕信息
    protected DisplayMetrics getDisplayMetrics(){
        return getResources().getDisplayMetrics();
    }
    protected int getWidthPixels(){
        return getDisplayMetrics().widthPixels;
    }
    protected int getHeightPixels(){
        return getDisplayMetrics().heightPixels;
    }
}
ListViewAdapter
public abstract class AbstractAdapter<T> extends BaseAdapter{
    //context mDatas LayoutInflater 
    private Context context;
    private List<T> listData;
    private LayoutInflater layoutInflater;
    //adapter的通用操作
    public AbstractAdapter(Context context,List<T> listData){
        this.context = context;
        this.listData = listData;
        layoutInflater = LayoutInflater.from(context);
    }
    @Override
    public int getCount() {
        return listData==null ? 0:listData.size();
    }
    @Override
    public Object getItem(int position) {
        return listData==null ? null:listData.get(position);
    }
    @Override
    public long getItemId(int position) {
        return position;
    }
    //datas相关
    public List<T> getListData() {
        return listData;
    }
    public void setListData(List<T> listData) {
        this.listData = listData;
    }

    //context相关
    public Context getContext(){
        return context;
    }

    //Inflate相关
    public LayoutInflater getLayoutInflater(){
        return layoutInflater;
    }
    public View inflate(@LayoutRes int layoutId,ViewGroup parent,boolean attachToRoot){
        return layoutInflater.inflate(layoutId,parent,attachToRoot);
    }
}
//进一步对getview进行抽取
public abstract class HolderAdapter<T,H> extends AbstractAdapter<T>{

    public HolderAdapter(Context context, List<T> listData) {
        super(context, listData);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        H holder = null;
        T t = getListData().get(position);
        if(convertView==null){
            convertView = buildConvertView(getLayoutInflater(),t,position,parent);
            holder = buildHolder(convertView,t,position);
            convertView.setTag(holder);
        }else{
            holder = (H)convertView.getTag();
        }
        bindViewDatas(holder,t,position);
        return convertView;
    }
    //创建ConvertView
    public abstract View buildConvertView(LayoutInflater layoutInflater,T t,int position, ViewGroup parent);
    //创建Hold对象
    public abstract H buildHolder(View convertView,T t,int position);
    //绑定数据
    public abstract void bindViewDatas(H holder,T t,int position);

}
//进一步对viewholder进行抽取
public abstract class ViewHolderAdapter<T> extends HolderAdapter<T,ViewHolder> {
    public ViewHolderAdapter(Context context, List<T> listData) {
        super(context, listData);
    }
    @Override
    public ViewHolder buildHolder(View convertView, T t, int position) {
        return new ViewHolder(convertView);
    }
}   

RecyclerViewAdapter的抽取

public abstract class HolderRecyclerAdapter<T,H extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<H>{
    //context datas layoutinflater
    private Context context;
    private List<T> listData;
    private LayoutInflater layoutInflater;
    //弥补Recyclerview的没有点击事件的缺点
    private OnItemClicklistener mOnItemClicklistener;

    //一些通用操作
    public HolderRecyclerAdapter(Context context, List<T> listData){
        super();
        this.context = context;
        this.listData = listData;
        this.layoutInflater = LayoutInflater.from(context);
    }
    @Override
    public int getItemCount() {
        return listData==null ? 0:listData.size();
    }
    public List<T> getListData() {
        return listData;
    }

    public void setListData(List<T> listData) {
        this.listData = listData;
    }
    @Override
    public H onCreateViewHolder(ViewGroup parent, int viewType) {
        View itemView = buildConvertView(layoutInflater,parent,viewType);
        return buildHolder(itemView,viewType);
    }

    @Override
    public void onBindViewHolder(H holder, final int position) {
        T t = position<listData.size() ? listData.get(position) : null;
        bindViewDatas(holder,t,position);
        if(this.mOnItemClicklistener!=null){
            holder.itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    mOnItemClicklistener.onItemClick(v,position);
                }
            });
        }
    }
    //点击事件相关
    public interface OnItemClicklistener{
        public void onItemClick(View v, int position);

    }
    public void setOnItemClicklistener(OnItemClicklistener onItemClicklistener){
        this.mOnItemClicklistener = onItemClicklistener;
    }
    //inflater相关
    public LayoutInflater getLayoutInflater(){
        return layoutInflater;
    }
    public View inflate(@LayoutRes int layoutId,ViewGroup parent,boolean attachToRoot){
        return layoutInflater.inflate(layoutId,parent,attachToRoot);
    }
    //context相关
    public Context getContext(){
        return context;
    }

    public abstract View buildConvertView(LayoutInflater layoutInflater,ViewGroup parent,int viewType);

    public abstract H buildHolder(View convertView,int viewType);

    public abstract void bindViewDatas(H holder,T t,int position);
}
Fragment的抽取
public abstract class BaseFragment extends Fragment implements BaseInterface {
    //context
    protected Context context;
    //dialog 和progressdialog
    private Dialog dialog;
    private BaseProgressDialog progressDialog;
    //Fragment的容器
    protected ViewGroup container;
    //Fragment的根布局
    protected View rootView;
    //当前页面 是否stop
    protected int curPage;
    protected boolean isStop;

    //获取Fragment的父容器
    public ViewGroup getContainer(){
        return container;
    }
    public View getActivityRootView(){
        //decorview中的content 还有一个title
        return getActivity().findViewById(android.R.id.content);
    }

    @Nullable
    @Override
    //细分为initUI initData addListeners
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        context = getActivity();
        this.container = container;
        rootView = inflater.inflate(inflaterRootView(), container, false);
        curPage = 1;
        initUI();
        initData();
        addListeners();
        if(rootView!=null)
            return rootView;
        return super.onCreateView(inflater, container, savedInstanceState);
    }
    //生命周期相关
    @Override
    public void onResume() {
        super.onResume();
        isStop = false;
    }

    @Override
    public void onStop() {
        super.onStop();
        isStop = true;
        dismissProgressDialog();
    }
    @Override
    public void onDestroy() {
        super.onDestroy();
        dismissDialog();
    }
    //intent相关
    protected Intent getIntent(){
        return getActivity().getIntent();
    }
    protected Intent getIntent(Class<?> cls){
        return new Intent(context,cls);
    }
    protected Intent getIntent(Class<?> cls,int flags){
        Intent intent = getIntent(cls);
        intent.setFlags(flags);
        return intent;
    }
    //页面跳转相关
    protected void startActivity(Class<?> cls){
        startActivity(getIntent(cls));
    }
    protected void startActivity(Class<?> cls,int flags){
        startActivity(getIntent(cls,flags));
    }
    protected void startActivityFinish(Class<?> cls){
        startActivity(cls);
        finish();
    }
    protected void startActivityFinish(Class<?> cls,int flags){
        startActivity(cls,flags);
        finish();
    }
    protected void finish(){
        getActivity().finish();
    }
    protected void setResult(int resultCode){
        setResult(resultCode,getIntent());
    }
    protected void setResult(int resultCode,Intent intent){
        getActivity().setResult(resultCode,intent);
    }
    protected void setIntent(Intent newIntent){
        getActivity().setIntent(newIntent);
    }

    //Inflate相关
    @Override
    public LayoutInflater getLayoutInflater(Bundle savedInstanceState) {
        return super.getLayoutInflater(savedInstanceState);
    }

    protected View inflate(@LayoutRes int id){
        return inflate(id,null);
    }

    protected View inflate(@LayoutRes int id, @Nullable ViewGroup root){
        return LayoutInflater.from(context).inflate(id,root);
    }

    //设置某一个view的监听器
    protected void setOnClickListener(@IdRes int id,View.OnClickListener onClicklistener){
        findView(id).setOnClickListener(onClicklistener);
    }
    protected <T extends View> T findView(int resId){
        return (T)rootView.findViewById(resId);
    }

    //Fragment替换相关
    public void replaceChildFragment(@IdRes int resId, Fragment fragment) {
        replaceFragment(getChildFragmentManager(),resId,fragment,false);
    }
    public void replaceFragment(@IdRes int resId, Fragment fragment){
        replaceFragment(resId,fragment,false);
    }
    public void replaceFragment(@IdRes int resId, Fragment fragment, boolean isBackStack) {
        replaceFragment(getFragmentManager(),resId,fragment,isBackStack);
    }
    public void replaceFragment(FragmentManager fragmentManager, @IdRes int resId, Fragment fragment, boolean isBackStack) {
        FragmentTransaction fragmentTransaction =  fragmentManager.beginTransaction();
        fragmentTransaction.replace(resId, fragment);
        if(isBackStack){
            fragmentTransaction.addToBackStack(null);
        }
        fragmentTransaction.commit();
    }
    //Toast相关
    protected void showToast(@StringRes  int resId){
        if(resId != NONE)
            ToastUtils.showToast(context,resId);
    }
    protected void showLongToast(@StringRes  int resId){
        if(resId != NONE)
            ToastUtils.showToast(context,resId, Toast.LENGTH_LONG);
    }
    protected void showToast(CharSequence text){
        ToastUtils.showToast(context,text);
    }
    protected void showLongToast(CharSequence text){
        ToastUtils.showToast(context,text, Toast.LENGTH_LONG);
    }
    //检查输入相关
    public boolean checkInput(TextView tv){
        return checkInput(tv,NONE);
    }

    public boolean checkInput(TextView tv,@StringRes int resId){
        return checkInput(tv,resId,false);
    }

    public boolean checkInput(TextView tv,@StringRes int resId,boolean isShake){
        if(StringUtils.isBlank(tv.getText())){
            if(isShake)
                startShake(tv,resId);
            else
                showToast(resId);
            return false;
        }
        return true;
    }
    public void startShake(View v,@StringRes int resId){
        startShake(v);
        showToast(resId);
    }
    public void startShake(View view){
        view.startAnimation(AnimationUtils.loadAnimation(context,R.anim.shake));
    }
    //软键盘相关
    public void hideInputMethod(final EditText v) {
        InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(v.getWindowToken(),0);

    }
    public void showInputMethod(final EditText v) {
        v.requestFocus();
        InputMethodManager imm = (InputMethodManager)context
                .getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.showSoftInput(v,0);
    }
    //progressDialog相关
    public Dialog getProgressDialog() {
        return progressDialog;
    }
    protected void showProgressDialog(){
        showProgressDialog(new ProgressBar(context));
    }
    protected void showProgressDialog(@LayoutRes int resId){
        showProgressDialog(LayoutInflater.from(context).inflate(resId,null));
    }
    protected void showProgressDialog(View v){
        dismissProgressDialog();
        progressDialog =  BaseProgressDialog.newInstance(context);
        progressDialog.setContentView(v);
        progressDialog.show();
    }
    //FragmentDialog相关
    public void showDialogFragment(DialogFragment dialogFragment){
        String tag = dialogFragment.getTag() !=null ? dialogFragment.getTag() : dialogFragment.getClass().getSimpleName();
        showDialogFragment(dialogFragment,tag);
    }
    public void showDialogFragment(DialogFragment dialogFragment,String tag) {
        dialogFragment.show(getFragmentManager(),tag);
    }
    protected void dismissDialogFragment(DialogFragment dialogFragment){
        if(dialogFragment != null){
            dialogFragment.dismiss();
        }
    }
    //dialog相关
    protected void showDialog(View contentView){
        showDialog(context,contentView);
    }
    protected void showDialog(Context context,View contentView){
        dismissDialog();
        dialog = new Dialog(context,R.style.dialog);
        dialog.setContentView(contentView);
        dialog.setCanceledOnTouchOutside(false);
        getDialogWindow(dialog);
        dialog.show();
    }
    protected void getDialogWindow(Dialog dialog){
        Window window = dialog.getWindow();
        WindowManager.LayoutParams lp = window.getAttributes();
        lp.width = (int)(getWidthPixels()*0.9f);
        window.setAttributes(lp);
    }
    public Dialog getDialog() {
        return dialog;
    }

    protected void dismissProgressDialog(){
        dismissDialog(progressDialog);
    }

    protected void dismissDialog(){
        dismissDialog(dialog);
    }

    protected void dismissDialog(Dialog dialog){
        if(dialog != null && dialog.isShowing()){
            dialog.dismiss();
        }
    }
    //开启一个异步任务
    protected void asyncThread(Runnable runnable){
        new Thread(runnable).start();
    }

    //屏幕信息相关
    protected DisplayMetrics getDisplayMetrics(){
        return getResources().getDisplayMetrics();
    }
    protected int getWidthPixels(){
        return getDisplayMetrics().widthPixels;
    }
    protected int getHeightPixels(){
        return getDisplayMetrics().heightPixels;
    }

    @LayoutRes
    public abstract int inflaterRootView();
}
FragmentPagerAdapter的抽取
public class ViewPagerFragmentAdapter extends FragmentPagerAdapter{

    private List<Fragment> listData;
    private List<CharSequence> listTitle;
    //构造器相关
    public ViewPagerFragmentAdapter(FragmentManager fm, List<Fragment> listData){
        this(fm,listData,null);
    }
    public ViewPagerFragmentAdapter(FragmentManager fm, List<Fragment> listData, List<CharSequence> listTitle) {
        super(fm);
        this.listData = listData;
        this.listTitle = listTitle;
    }

    //datas相关
    public List<Fragment> getListData() {
        return listData;
    }
    public void setListData(List<Fragment> listData) {
        this.listData = listData;
    }
    //titles相关
    public List<CharSequence> getListTitle() {
        return listTitle;
    }
    public void setListTitle(List<CharSequence> listTitle) {
        this.listTitle = listTitle;
    }
    //通用操作
    @Override
    public Fragment getItem(int position) {
        return listData==null ? null : listData.get(position) ;
    }
    @Override
    public int getCount() {
        return listData == null ? 0 : listData.size();
    }
    @Override
    public CharSequence getPageTitle(int position) {
        if(listTitle!=null && listTitle.size()!=0){
            return listTitle.get(position);
        }
        return super.getPageTitle(position);
    }
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值