編輯:關於Android編程
最近呢,本人辭職了,在找工作期間,不幸碰到了這個求職淡季,另外還是大學生畢業求職的高峰期,簡歷發了無數份卻都石沉大海,寶寶心裡那是一個苦啊!翻著過去的代碼,本人偶然找到了一個有意思的控件,那時本人還沒有寫博客的習慣,現在補上,先看效果圖:
然後看用法代碼:
StellarMap stellarMap = (StellarMap) findViewById(R.id.stellar); // 設置數據 RecommendAdapter adapter = new RecommendAdapter(); stellarMap.setAdapter(adapter); // 首頁選中 stellarMap.setGroup(0, true); // 拆分屏幕 stellarMap.setRegularity(15, 20);
class RecommendAdapter implements Adapter { /** 默認組數 */ public static final int PAGESIZE = 15; @Override public int getGroupCount() { // 數據分組 int groupCount = data.size() / PAGESIZE; // 最後一組 if (data.size() % PAGESIZE != 0) { return groupCount + 1; } return groupCount; } @Override public int getCount(int group) { // 最後一組 if (data.size() % PAGESIZE != 0) { if (group == getGroupCount() - 1) { return data.size() % PAGESIZE; } } return PAGESIZE; } @Override public View getView(int group, int position, View convertView) { TextView tv = new TextView(MainActivity.this); int index = group * PAGESIZE + position; tv.setText(data.get(index)); // 隨機大小 Random random = new Random(); // 14-17 int size = random.nextInt(4) + 14; tv.setTextSize(size); // 隨機顏色 int alpha = 255; int red = random.nextInt(190) + 30; int green = random.nextInt(190) + 30; int blue = random.nextInt(190) + 30; int argb = Color.argb(alpha, red, green, blue); tv.setTextColor(argb); return tv; } @Override public int getNextGroupOnPan(int group, float degree) { if(group == getGroupCount() - 1){ group = -1; } return group + 1; } @Override public int getNextGroupOnZoom(int group, boolean isZoomIn) { if(group == getGroupCount() - 1){ group = -1; } return group + 1; } }
接下來才是正餐,我們看看StellarMap的實現,StellarMap繼承於FrameLayout:
/** 構造方法 */ public StellarMap(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } public StellarMap(Context context, AttributeSet attrs) { super(context, attrs); init(); } public StellarMap(Context context) { super(context); init(); }
/** 初始化方法 */ private void init() { mGroupCount = 0; mHidenGroupIndex = -1; mShownGroupIndex = -1; mHidenGroup = new RandomLayout(getContext()); mShownGroup = new RandomLayout(getContext()); addView(mHidenGroup, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); mHidenGroup.setVisibility(View.GONE); addView(mShownGroup, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); mGestureDetector = new GestureDetector(this); setOnTouchListener(this); // 設置動畫 mZoomInNearAnim = AnimationUtil.createZoomInNearAnim(); mZoomInNearAnim.setAnimationListener(this); mZoomInAwayAnim = AnimationUtil.createZoomInAwayAnim(); mZoomInAwayAnim.setAnimationListener(this); mZoomOutNearAnim = AnimationUtil.createZoomOutNearAnim(); mZoomOutNearAnim.setAnimationListener(this); mZoomOutAwayAnim = AnimationUtil.createZoomOutAwayAnim(); mZoomOutAwayAnim.setAnimationListener(this); }
按照代碼執行順序來,下一步是設置Adapter:
/** 設置本Adapter */ public void setAdapter(Adapter adapter) { mAdapter = adapter; mGroupCount = mAdapter.getGroupCount(); if (mGroupCount > 0) { mShownGroupIndex = 0; } setChildAdapter(); }
/** 為子Group設置Adapter */ private void setChildAdapter() { if (null == mAdapter) { return; } mHidenGroupAdapter = new RandomLayout.Adapter() { // 取出本Adapter的View對象給HidenGroup的Adapter @Override public View getView(int position, View convertView) { return mAdapter.getView(mHidenGroupIndex, position, convertView); } @Override public int getCount() { return mAdapter.getCount(mHidenGroupIndex); } }; mHidenGroup.setAdapter(mHidenGroupAdapter); mShownGroupAdapter = new RandomLayout.Adapter() { // 取出本Adapter的View對象給ShownGroup的Adapter @Override public View getView(int position, View convertView) { return mAdapter.getView(mShownGroupIndex, position, convertView); } @Override public int getCount() { return mAdapter.getCount(mShownGroupIndex); } }; mShownGroup.setAdapter(mShownGroupAdapter); }
/** 構造方法 */ public RandomLayout(Context context) { super(context); init(); }
/** 初始化方法 */ private void init() { mLayouted = false; mRdm = new Random(); setRegularity(1, 1); mFixedViews = new HashSet(); mRecycledViews = new LinkedList (); }
/** 設置數據源 */ public void setAdapter(Adapter adapter) { this.mAdapter = adapter; }
/** 給指定的Group設置動畫 */ public void setGroup(int groupIndex, boolean playAnimation) { switchGroup(groupIndex, playAnimation, mZoomInNearAnim, mZoomInAwayAnim); }
/** 給下一個Group設置進出動畫 */ private void switchGroup(int newGroupIndex, boolean playAnimation, Animation inAnim, Animation outAnim) { if (newGroupIndex < 0 || newGroupIndex >= mGroupCount) { return; } // 把當前顯示Group角標設置為隱藏的 mHidenGroupIndex = mShownGroupIndex; // 把下一個Group角標設置為顯示的 mShownGroupIndex = newGroupIndex; // 交換兩個Group RandomLayout temp = mShownGroup; mShownGroup = mHidenGroup; mShownGroup.setAdapter(mShownGroupAdapter); mHidenGroup = temp; mHidenGroup.setAdapter(mHidenGroupAdapter); // 刷新顯示的Group mShownGroup.refresh(); // 顯示Group mShownGroup.setVisibility(View.VISIBLE); // 啟動動畫 if (playAnimation) { if (mShownGroup.hasLayouted()) { mShownGroup.startAnimation(inAnim); } mHidenGroup.startAnimation(outAnim); } else { mHidenGroup.setVisibility(View.GONE); } }
最後一行代碼,stellarMap.setRegularity(15, 20)方法:
/** 設置隱藏組和顯示組的x和y的規則 */ public void setRegularity(int xRegularity, int yRegularity) { mHidenGroup.setRegularity(xRegularity, yRegularity); mShownGroup.setRegularity(xRegularity, yRegularity); }
/** 設置mXRegularity和mXRegularity,確定區域的個數 */ public void setRegularity(int xRegularity, int yRegularity) { if (xRegularity > 1) { this.mXRegularity = xRegularity; } else { this.mXRegularity = 1; } if (yRegularity > 1) { this.mYRegularity = yRegularity; } else { this.mYRegularity = 1; } this.mAreaCount = mXRegularity * mYRegularity;// 個數等於x方向的個數*y方向的個數 this.mAreaDensity = new int[mYRegularity][mXRegularity];// 存放區域的二維數組 }
/** 確定子View的位置,這個就是區域分布的關鍵 */ @Override public void onLayout(boolean changed, int l, int t, int r, int b) { final int count = getChildCount(); // 確定自身的寬高 int thisW = r - l - this.getPaddingLeft() - this.getPaddingRight(); int thisH = b - t - this.getPaddingTop() - this.getPaddingBottom(); // 自身內容區域的右邊和下邊 int contentRight = r - getPaddingRight(); int contentBottom = b - getPaddingBottom(); // 按照順序存放把區域存放到集合中 ListavailAreas = new ArrayList (mAreaCount); for (int i = 0; i < mAreaCount; i++) { availAreas.add(i); } int areaCapacity = (count + 1) / mAreaCount + 1; // 區域密度,表示一個區域內可以放幾個View,+1表示至少要放一個 int availAreaCount = mAreaCount; // 可用的區域個數 for (int i = 0; i < count; i++) { final View child = getChildAt(i); if (child.getVisibility() == View.GONE) { // gone掉的view是不參與布局 continue; } if (!mFixedViews.contains(child)) {// mFixedViews用於存放已經確定好位置的View,存到了就沒必要再次存放 LayoutParams params = (LayoutParams) child.getLayoutParams(); // 先測量子View的大小 int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(this.getMeasuredWidth(), MeasureSpec.AT_MOST);// 為子View准備測量的參數 int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(this.getMeasuredHeight(), MeasureSpec.AT_MOST); child.measure(childWidthMeasureSpec, childHeightMeasureSpec); // 子View測量之後的寬和高 int childW = child.getMeasuredWidth(); int childH = child.getMeasuredHeight(); // 用自身的高度去除以分配值,可以算出每一個區域的寬和高 float colW = thisW / (float) mXRegularity; float rowH = thisH / (float) mYRegularity; while (availAreaCount > 0) { // 如果使用區域大於0,就可以為子View嘗試分配 int arrayIdx = mRdm.nextInt(availAreaCount);// 隨機一個list中的位置 int areaIdx = availAreas.get(arrayIdx);// 再根據list中的位置獲取一個區域編號 int col = areaIdx % mXRegularity;// 計算出在二維數組中的位置 int row = areaIdx / mXRegularity; if (mAreaDensity[row][col] < areaCapacity) {// 區域密度未超過限定,將view置入該區域 int xOffset = (int) colW - childW; // 區域寬度 和 子View的寬度差值,差值可以用來做區域內的位置隨機 if (xOffset <= 0) {// 說明子View的寬比較大 xOffset = 1; } int yOffset = (int) rowH - childH; if (yOffset <= 0) {// 說明子View的高比較大 yOffset = 1; } // 確定左邊,等於區域寬度*左邊的區域 params.mLeft = getPaddingLeft() + (int) (colW * col + mRdm.nextInt(xOffset)); int rightEdge = contentRight - childW; if (params.mLeft > rightEdge) {// 加上子View的寬度後不能超出右邊界 params.mLeft = rightEdge; } params.mRight = params.mLeft + childW; params.mTop = getPaddingTop() + (int) (rowH * row + mRdm.nextInt(yOffset)); int bottomEdge = contentBottom - childH; if (params.mTop > bottomEdge) {// 加上子View的寬度後不能超出右邊界 params.mTop = bottomEdge; } params.mBottom = params.mTop + childH; if (!isOverlap(params)) {// 判斷是否和別的View重疊了 mAreaDensity[row][col]++;// 沒有重疊,把該區域的密度加1 child.layout(params.mLeft, params.mTop, params.mRight, params.mBottom);// 布局子View mFixedViews.add(child);// 添加到已經布局的集合中 break; } else {// 如果重疊了,把該區域移除, availAreas.remove(arrayIdx); availAreaCount--; } } else {// 區域密度超過限定,將該區域從可選區域中移除 availAreas.remove(arrayIdx); availAreaCount--; } } } } mLayouted = true; }
在StellarMap中加入了手勢,用於用戶滑動的時候給與交互:
@Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { int centerX = getMeasuredWidth() / 2; int centerY = getMeasuredWidth() / 2; int x1 = (int) e1.getX() - centerX; int y1 = (int) e1.getY() - centerY; int x2 = (int) e2.getX() - centerX; int y2 = (int) e2.getY() - centerY; if ((x1 * x1 + y1 * y1) > (x2 * x2 + y2 * y2)) { zoomOut(); } else { zoomIn(); } return true; }
/** 給Group設置動畫入 */ public void zoomIn() { final int nextGroupIndex = mAdapter.getNextGroupOnZoom(mShownGroupIndex, true); switchGroup(nextGroupIndex, true, mZoomInNearAnim, mZoomInAwayAnim); } /** 給Group設置出動畫 */ public void zoomOut() { final int nextGroupIndex = mAdapter.getNextGroupOnZoom(mShownGroupIndex, false); switchGroup(nextGroupIndex, true, mZoomOutNearAnim, mZoomOutAwayAnim); }
類似QQ分組的樣子,實現tableView的折疊與展開。其實要做這個效果我先想到的是在tableView中再嵌套多個tableView,這個想法實現起來就有點難了。所以還
說明第一下:按照前面的方式我們創建了項目,如果使用的是最新的ADT,Minimum Android SDK選的是android2.*或1.*,此時會默認創建一個兼容的項目
上一篇我們主要了解了為什麼適配,以及怎麼適配,同時給出了部分切圖規范,和在開發過程中需要的一些注意事項,這一遍主要從官方給出的指導建議出發,從視覺的角度來說說怎麼適配。度
以前接觸過NDK的開發,是在Eclipse環境下開發的。今天嘗試了下用Android Studio來配置,結果真是處處都是坑,現在總結一下:一、步驟1. 首先創建Main