編輯:關於Android編程
國際慣例,先來效果圖

在閱讀本文章之前,請確定熟悉【Scroller】相關的知識,如果不熟悉,請小伙伴兒先百度後再來吧。
假如你已經知道【Scroller】了,那麼就接著往下看吧。
首先,我們把側拉菜單的構造給解析出來。多次觀看上面的效果圖,我們可以得出以下的結論。
整體可以看做是一個ViewGroup,這個ViewGroup包含了最多三個子View(分別是左菜單的紅色View、中間正文內容的白色View、右菜單的藍色View);三個子View(我稱為UI界面,因為代碼中的Java類就取名這個)的移動是在ViewGroup的onTouchEvent方法中控制;每個UI界面都擁有獨特的東西,比如子控件布局,因此我們希望用R.layout.*的方式引入;每個UI界面又都擁有相同的屬性,比如都有寬度屬性,滑動臨界值屬性,那麼就可以用一個超類來封裝所有相似的東西;最最重要的地方,動態計算出scrollX的值,然後用Scroller來滑動。 理清楚了結構後,我們來開始第一步的設計,也就是封裝超類,首先給出代碼:
/**
* Created by ccwxf on 2016/6/14.
*/
public abstract class UI {
protected Context context;
//當前UI界面的布局文件
protected View contentView;
//當前UI界面在父控件的起點X坐標
protected int startX;
//當前UI界面在父控件的終點X坐標
protected int stopX;
//當前UI界面的寬度
protected int width;
protected UI(Context context, View contentView){
this.context = context;
this.contentView = contentView;
}
protected abstract void calculate(float leftScale, float rightScale);
protected void show(Scroller mScroller){
if(mScroller != null){
mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), startX - mScroller.getFinalX(), 0);
}
}
}
這個UI超類就用於模擬每一個界面,其中主要封裝了內容View的設置、跳轉界面的邏輯代碼,以及暴露出去需要子類實現的calculate方法,這個calculate方法主要是要計算startX、stopX、width以及各子類獨有的屬性。
/**
* Created by ccwxf on 2016/6/14.
*/
public class LeftMenuUI extends UI {
// 是指要打開該UI界面所需要滾動的X坐標臨界值
public int openX;
// 是指要關閉該UI界面所需要的滾動的X坐標臨界值
public int closeX;
public LeftMenuUI(Context context, View contentView) {
super(context, contentView);
}
@Override
protected void calculate(float leftScale, float rightScale) {
startX = 0;
stopX = (int) (Util.getScreenWidth(context) * leftScale);
this.width = stopX - startX;
this.openX = (int) (startX + (1 - SideLayout.DEFAULT_SIDE) * this.width);
this.closeX = (int) (startX + SideLayout.DEFAULT_SIDE * this.width);
}
}
首先黑色框代表的是整個的布局,被分為了三個部分,分別是左菜單、正文內容、右菜單。紅色框代表的是手機的屏幕,默認手機屏幕的寬高和正文內容的寬高都是一樣的。因此圖上所示是重合的。
那麼問題來了,途中所示的綠色橫線代表的openX和closeX分別是什麼意思呢?我們假想一下,我們現在正處於正文的內容,此時手指向右滑屏,將滑出左菜單的部分,此時紅框代表的屏幕就會向左移動(如果聽不懂就真的需要先了解Scroller的使用喲),如果紅色框移動到openX這個綠線的左邊,我們就認為超出了滑動的臨界值,判斷為顯示左菜單的操作,現在應該明白了openX的意思了吧,就是超過這個值就顯示左菜單。
那麼問題又來了,closeX怎麼解釋呢?我們再次假象一下,我們現在正處於左菜單,此時我們向左滑動屏幕,如果紅色框從0開始向右移動,如果超出了closeX這個臨界值,就代表我們要滑出左菜單進入正文內容,這就是closeX的意思。
好了,理解了左菜單這個類,那麼正文內容和右菜單也同樣好理解了。接下來給出正文類:ContentUI
public class ContentUI extends UI {
public ContentUI(Context context, View contentView) {
super(context, contentView);
}
@Override
protected void calculate(float leftScale, float rightScale) {
int width = Util.getScreenWidth(context);
int leftWidth = (int) (width * leftScale);
startX = leftWidth;
stopX = leftWidth + width;
this.width = stopX - startX;
}
}
/**
* Created by ccwxf on 2016/6/14.
*/
public class RightMenuUI extends UI {
// 是指要打開該UI界面所需要滾動的X坐標臨界值
public int openX;
// 是指要關閉該UI界面所需要的滾動的X坐標臨界值
public int closeX;
public RightMenuUI(Context context, View contentView) {
super(context, contentView);
}
@Override
protected void calculate(float leftScale, float rightScale) {
int width = Util.getScreenWidth(context);
startX = (int) (width * (1 + leftScale));
stopX = (int) (width * (1 + leftScale + rightScale));
this.width = stopX - startX;
this.openX = (int) (startX - width + SideLayout.DEFAULT_SIDE * this.width);
this.closeX = (int) (startX - width + (1 - SideLayout.DEFAULT_SIDE) * this.width);
}
/**
* 必須重載父類方法,因為滑動的起點是從0開始
*/
protected void show(Scroller mScroller, int measureWidth){
if(mScroller != null){
mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), measureWidth - Util.getScreenWidth(context) - mScroller.getFinalX(), 0);
}
}
}
/**
* Created by ccwxf on 2016/6/14.
*/
public class SideLayout extends LinearLayout {
//默認的菜單寬度與屏幕寬度的比值
public static final float DEFAULT_SCALE = 0.66f;
//默認的滑動切換閥值相對於菜單寬度的比值
public static final float DEFAULT_SIDE = 0.25f;
private Scroller mScroller;
//三個UI界面
private LeftMenuUI leftMenuUI;
private ContentUI contentUI;
private RightMenuUI rightMenuUI;
//左菜單和右菜單相對於屏幕的比值
private float leftScale = 0;
private float rightScale = 0;
//控件的測量寬度
private float measureWidth = 0;
//手指Touch時的X坐標和移動時的X坐標
private float mTouchX;
private float mMoveX;
public SideLayout(Context context) {
super(context);
init();
}
public SideLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public SideLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
mScroller = new Scroller(getContext());
setOrientation(LinearLayout.HORIZONTAL);
}
/**
* 設置左菜單的布局
* @param view 左菜單布局
* @return 返回當類
*/
public SideLayout setLeftMenuView(View view){
return setLeftMenuView(view, DEFAULT_SCALE);
}
public SideLayout setLeftMenuView(View view, float leftScale){
leftMenuUI = new LeftMenuUI(getContext(), view);
this.leftScale = leftScale;
return this;
}
/**
* 設置右菜單的布局
* @param view 右菜單布局
* @return 當類
*/
public SideLayout setRightMenuView(View view){
return setRightMenuView(view, DEFAULT_SCALE);
}
public SideLayout setRightMenuView(View view, float rightScale){
rightMenuUI = new RightMenuUI(getContext(), view);
this.rightScale = rightScale;
return this;
}
/**
* 設置正文布局
* @param view 正文布局
* @return 返回當類
*/
public SideLayout setContentView(View view){
contentUI = new ContentUI(getContext(), view);
return this;
}
/**
* 提交配置,必須調用
*/
public void commit() {
removeAllViews();
if(leftMenuUI != null){
leftMenuUI.calculate(leftScale, rightScale);
measureWidth += leftMenuUI.width;
addView(leftMenuUI.contentView, new LayoutParams(leftMenuUI.width, LayoutParams.MATCH_PARENT));
}
if(contentUI != null){
contentUI.calculate(leftScale, rightScale);
measureWidth += contentUI.width;
addView(contentUI.contentView, new LayoutParams(contentUI.width, LayoutParams.MATCH_PARENT));
}
if(rightMenuUI != null){
rightMenuUI.calculate(leftScale, rightScale);
measureWidth += rightMenuUI.width;
addView(rightMenuUI.contentView, new LayoutParams(rightMenuUI.width, LayoutParams.MATCH_PARENT));
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
mTouchX = event.getX();
mMoveX = event.getX();
return true;
case MotionEvent.ACTION_MOVE:
int dx = (int) (event.getX() - mMoveX);
if(dx > 0){
//右滑
if(mScroller.getFinalX() > 0){
mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), -dx, 0);
}else{
mScroller.setFinalX(0);
}
}else{
//左滑
if(mScroller.getFinalX() + Util.getScreenWidth(getContext()) - dx < measureWidth){
mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), -dx, 0);
}else{
mScroller.setFinalX((int) (measureWidth - Util.getScreenWidth(getContext())));
}
}
mMoveX = event.getX();
invalidate();
break;
case MotionEvent.ACTION_UP:
toTargetUI((int) (event.getX() - mTouchX));
break;
}
return super.onTouchEvent(event);
}
/**
* 滑動切換到目標的UI界面
* @param dx 手指抬起時相比手指落下,滑動的距離
*/
private void toTargetUI(int dx){
int scrollX = mScroller.getFinalX();
if(dx > 0){
//右滑
if(leftMenuUI != null){
if(scrollX >= leftMenuUI.openX && scrollX < leftMenuUI.stopX){
contentUI.show(mScroller);
}else if(scrollX >= leftMenuUI.startX && scrollX < leftMenuUI.openX){
leftMenuUI.show(mScroller);
}
}
if(rightMenuUI != null){
if(scrollX >= rightMenuUI.closeX){
rightMenuUI.show(mScroller, (int) measureWidth);
}else if(scrollX >= contentUI.startX && scrollX < rightMenuUI.closeX){
contentUI.show(mScroller);
}
}
}else{
//左滑
if(leftMenuUI != null){
if(scrollX > leftMenuUI.startX && scrollX <= leftMenuUI.closeX){
leftMenuUI.show(mScroller);
}else if(scrollX > leftMenuUI.closeX && scrollX < leftMenuUI.stopX){
contentUI.show(mScroller);
}
}
if(rightMenuUI != null){
if(scrollX > contentUI.startX && scrollX <= rightMenuUI.openX){
contentUI.show(mScroller);
}else if(scrollX > rightMenuUI.openX){
rightMenuUI.show(mScroller, (int) measureWidth);
}
}
}
}
@Override
public void computeScroll(){
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
postInvalidate();
}
super.computeScroll();
}
}
/**
* 滑動切換到目標的UI界面
* @param dx 手指抬起時相比手指落下,滑動的距離
*/
private void toTargetUI(int dx){
int scrollX = mScroller.getFinalX();
if(dx > 0){
//右滑
if(leftMenuUI != null){
if(scrollX >= leftMenuUI.openX && scrollX < leftMenuUI.stopX){
contentUI.show(mScroller);
}else if(scrollX >= leftMenuUI.startX && scrollX < leftMenuUI.openX){
leftMenuUI.show(mScroller);
}
}
if(rightMenuUI != null){
if(scrollX >= rightMenuUI.closeX){
rightMenuUI.show(mScroller, (int) measureWidth);
}else if(scrollX >= contentUI.startX && scrollX < rightMenuUI.closeX){
contentUI.show(mScroller);
}
}
}else{
//左滑
if(leftMenuUI != null){
if(scrollX > leftMenuUI.startX && scrollX <= leftMenuUI.closeX){
leftMenuUI.show(mScroller);
}else if(scrollX > leftMenuUI.closeX && scrollX < leftMenuUI.stopX){
contentUI.show(mScroller);
}
}
if(rightMenuUI != null){
if(scrollX > contentUI.startX && scrollX <= rightMenuUI.openX){
contentUI.show(mScroller);
}else if(scrollX > rightMenuUI.openX){
rightMenuUI.show(mScroller, (int) measureWidth);
}
}
}
}
/**
* Created by ccwxf on 2016/6/14.
*/
public class Util {
public static int getScreenWidth(Context context){
return context.getResources().getDisplayMetrics().widthPixels;
}
}
)
public class MainActivity extends Activity {
private View leftView;
private View contentView;
private View rightView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initUI();
SideLayout sideLayout = (SideLayout) findViewById(R.id.sideLayout);
sideLayout.setLeftMenuView(leftView).setContentView(contentView).setRightMenuView(rightView).commit();
}
private void initUI(){
leftView = View.inflate(this, R.layout.left, null);
contentView = View.inflate(this, R.layout.content, null);
rightView = View.inflate(this, R.layout.right, null);
//初始化左邊菜單
ListView listView = (ListView) leftView.findViewById(R.id.listView);
listView.setAdapter(new ArrayAdapter(this, android.R.layout.simple_list_item_1, new String[]{
"123","456","789","101","112","123","456","789","101","112","123","456","789","101","112","123","456","789","101","112"
}));
//初始化正文內容
ViewPager viewPager = (ViewPager) contentView.findViewById(R.id.viewPager);
viewPager.setAdapter(new TestDemoAdapter());
}
public class TestDemoAdapter extends PagerAdapter{
private ImageView[] imageViews = new ImageView[5];
public TestDemoAdapter() {
for(int i = 0; i < imageViews.length; i++){
imageViews[i] = new ImageView(MainActivity.this);
imageViews[i].setImageResource(R.mipmap.ic_launcher);
}
}
@Override
public int getCount() {
return 5;
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
container.addView(imageViews[position]);
return imageViews[position];
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView(imageViews[position]);
}
}
}
Android Studio -修改LogCat的顏色*美爆了*
一、 先看效果二、設置 File->Settings 或Ctrl + Alt +S 找到 Editor -> Colors &Fonts ->
Android ShareSDK快速實現分享功能
第一步 :獲取ShareSDK 為了集成ShareSDK,您首先需要到ShareSDK官方網站注冊並且創建應用,獲得ShareSDK的Appkey,然後到SDK的下載頁
Android應用開發中Fragment的靜態加載與動態加載實例
1、Fragment的靜態使用Fragment是作為Activity的UI的一部分,它內嵌在Activity中,多個Fragment可以把一個Activity分成多個部分
Android 從源碼的角度分析——為什麼要用newInstance來實例化Fragment
最近在看Google技術文檔的時候發現了一種新的方式來實例化Fragment,就是采用靜態工廠的方式創建Fragment。我們在使用Android studio創建一個類