Android高階圖片滾動控制元件,編寫3D版的圖片輪播器
大家好,好久不見了,最近由於工作特別繁忙,已經有一個多月的時間沒寫部落格了,我也是深感慚愧。那麼今天的這篇既然是闊別了一個多月的文章,當然要帶來更加給力點的內容了,那麼話不多說,趕快進入到今天的正題吧。
說到圖片輪播器,很多的Android應用中都會帶有這個功能,比如說網易新聞、淘寶等。最新我們公司的一款應用也加入了這個功能,並且在圖片輪播的基礎上還增加了三維立體的效果,但比較遺憾的是,整體效果並不理想,使用者體驗性比較糟糕。因此,我就花了點時間去編寫了一個效果更好的3D圖片輪播器,自我感覺還是比較滿意的,這裡果斷寫一篇部落格來分享給大家。
首先來介紹一下實現原理吧,傳統的圖片輪播器在一個介面上只會顯示一張圖片,要用手指進行左右滑動才能看到其它的圖片。這裡我們將思維發散一下,允許在一個介面上同時顯示三張圖片,再通過Camera的方式對左右的兩張圖進行3D旋轉,這樣就能製作出一種立體的圖片輪播器了,原理示意圖如下所示:
對圖片進行立體操作還是要使用到Camera技術,如果你對這個技術還不太熟悉,可以到網上搜一些相關資料,或者參考我前面的一篇文章:Android中軸旋轉特效實現,製作別樣的圖片瀏覽器 。
那麼我們現在就開始動手吧,首先新建一個Android專案,起名叫做ImageSwitchViewTest。
然後新建一個Image3DView繼承自ImageView,它會繼承ImageView的所有屬性,並且加入3D旋轉的功能,程式碼如下所示:
public class Image3DView extends ImageView { /** * 旋轉角度的基準值 */ private static final float BASE_DEGREE = 50f; /** * 旋轉深度的基準值 */ private static final float BASE_DEEP = 150f; private Camera mCamera; private Matrix mMaxtrix; private Bitmap mBitmap; /** * 當前圖片對應的下標 */ private int mIndex; /** * 在前圖片在X軸方向滾動的距離 */ private int mScrollX; /** * Image3DSwitchView控制元件的寬度 */ private int mLayoutWidth; /** * 當前圖片的寬度 */ private int mWidth; /** * 當前旋轉的角度 */ private float mRotateDegree; /** * 旋轉的中心點 */ private float mDx; /** * 旋轉的深度 */ private float mDeep; public Image3DView(Context context, AttributeSet attrs) { super(context, attrs); mCamera = new Camera(); mMaxtrix = new Matrix(); } /** * 初始化Image3DView所需要的資訊,包括圖片寬度,擷取背景圖等。 */ public void initImageViewBitmap() { if (mBitmap == null) { setDrawingCacheEnabled(true); buildDrawingCache(); mBitmap = getDrawingCache(); } mLayoutWidth = Image3DSwitchView.mWidth; mWidth = getWidth() + Image3DSwitchView.IMAGE_PADDING * 2; } /** * 設定旋轉角度。 * * @param index * 當前圖片的下標 * @param scrollX * 當前圖片在X軸方向滾動的距離 */ public void setRotateData(int index, int scrollX) { mIndex = index; mScrollX = scrollX; } /** * 回收當前的Bitmap物件,以釋放記憶體。 */ public void recycleBitmap() { if (mBitmap != null && !mBitmap.isRecycled()) { mBitmap.recycle(); } } @Override public void setImageResource(int resId) { super.setImageResource(resId); mBitmap = null; initImageViewBitmap(); } @Override public void setImageBitmap(Bitmap bm) { super.setImageBitmap(bm); mBitmap = null; initImageViewBitmap(); } @Override public void setImageDrawable(Drawable drawable) { super.setImageDrawable(drawable); mBitmap = null; initImageViewBitmap(); } @Override public void setImageURI(Uri uri) { super.setImageURI(uri); mBitmap = null; initImageViewBitmap(); } @Override protected void onDraw(Canvas canvas) { if (mBitmap == null) { // 如果Bitmap物件還不存在,先使用父類的onDraw方法進行繪製 super.onDraw(canvas); } else { if (isImageVisible()) { // 繪圖時需要注意,只有當圖片可見的時候才進行繪製,這樣可以節省運算效率 computeRotateData(); mCamera.save(); mCamera.translate(0.0f, 0.0f, mDeep); mCamera.rotateY(mRotateDegree); mCamera.getMatrix(mMaxtrix); mCamera.restore(); mMaxtrix.preTranslate(-mDx, -getHeight() / 2); mMaxtrix.postTranslate(mDx, getHeight() / 2); canvas.drawBitmap(mBitmap, mMaxtrix, null); } } } /** * 在這裡計算所有旋轉所需要的資料。 */ private void computeRotateData() { float degreePerPix = BASE_DEGREE / mWidth; float deepPerPix = BASE_DEEP / ((mLayoutWidth - mWidth) / 2); switch (mIndex) { case 0: mDx = mWidth; mRotateDegree = 360f - (2 * mWidth + mScrollX) * degreePerPix; if (mScrollX < -mWidth) { mDeep = 0; } else { mDeep = (mWidth + mScrollX) * deepPerPix; } break; case 1: if (mScrollX > 0) { mDx = mWidth; mRotateDegree = (360f - BASE_DEGREE) - mScrollX * degreePerPix; mDeep = mScrollX * deepPerPix; } else { if (mScrollX < -mWidth) { mDx = -Image3DSwitchView.IMAGE_PADDING * 2; mRotateDegree = (-mScrollX - mWidth) * degreePerPix; } else { mDx = mWidth; mRotateDegree = 360f - (mWidth + mScrollX) * degreePerPix; } mDeep = 0; } break; case 2: if (mScrollX > 0) { mDx = mWidth; mRotateDegree = 360f - mScrollX * degreePerPix; mDeep = 0; if (mScrollX > mWidth) { mDeep = (mScrollX - mWidth) * deepPerPix; } } else { mDx = -Image3DSwitchView.IMAGE_PADDING * 2; mRotateDegree = -mScrollX * degreePerPix; mDeep = 0; if (mScrollX < -mWidth) { mDeep = -(mWidth + mScrollX) * deepPerPix; } } break; case 3: if (mScrollX < 0) { mDx = -Image3DSwitchView.IMAGE_PADDING * 2; mRotateDegree = BASE_DEGREE - mScrollX * degreePerPix; mDeep = -mScrollX * deepPerPix; } else { if (mScrollX > mWidth) { mDx = mWidth; mRotateDegree = 360f - (mScrollX - mWidth) * degreePerPix; } else { mDx = -Image3DSwitchView.IMAGE_PADDING * 2; mRotateDegree = BASE_DEGREE - mScrollX * degreePerPix; } mDeep = 0; } break; case 4: mDx = -Image3DSwitchView.IMAGE_PADDING * 2; mRotateDegree = (2 * mWidth - mScrollX) * degreePerPix; if (mScrollX > mWidth) { mDeep = 0; } else { mDeep = (mWidth - mScrollX) * deepPerPix; } break; } } /** * 判斷當前圖片是否可見。 * * @return 當前圖片可見返回true,不可見返回false。 */ private boolean isImageVisible() { boolean isVisible = false; switch (mIndex) { case 0: if (mScrollX < (mLayoutWidth - mWidth) / 2 - mWidth) { isVisible = true; } else { isVisible = false; } break; case 1: if (mScrollX > (mLayoutWidth - mWidth) / 2) { isVisible = false; } else { isVisible = true; } break; case 2: if (mScrollX > mLayoutWidth / 2 + mWidth / 2 || mScrollX < -mLayoutWidth / 2 - mWidth / 2) { isVisible = false; } else { isVisible = true; } break; case 3: if (mScrollX < -(mLayoutWidth - mWidth) / 2) { isVisible = false; } else { isVisible = true; } break; case 4: if (mScrollX > mWidth - (mLayoutWidth - mWidth) / 2) { isVisible = true; } else { isVisible = false; } break; } return isVisible; } }
這段程式碼比較長,也比較複雜的,我們慢慢來分析。在Image3DView的建構函式中初始化了一個Camera和Matrix物件,用於在後面對圖片進行3D操作。然後在initImageViewBitmap()方法中初始化了一些必要的資訊,比如對當前圖片進行截圖,以用於後續的立體操作,得到當前圖片的寬度等。
然後還提供了一個setRotateData()方法,用於設定當前圖片的下標和滾動距離,有了這兩樣資料就可以通過computeRotateData()方法來計算旋轉角度的一些資料,以及通過isImageVisible()方法來判斷出當前圖片是否可見了,具體詳細的演算法邏輯你可以閱讀程式碼來慢慢分析。
接下來當圖片需要繪製到螢幕上的時候就會呼叫onDraw()方法,在onDraw()方法中會進行判斷,如果當前圖片可見就呼叫computeRotateData()方法來計算旋轉時所需要的各種資料,之後再通過Camera和Matrix來執行旋轉操作就可以了。
接著新建一個Image3DSwitchView繼承自ViewGroup,程式碼如下所示:
public class Image3DSwitchView extends ViewGroup {
/**
* 圖片左右兩邊的空白間距
*/
public static final int IMAGE_PADDING = 10;
private static final int TOUCH_STATE_REST = 0;
private static final int TOUCH_STATE_SCROLLING = 1;
/**
* 滾動到下一張圖片的速度
*/
private static final int SNAP_VELOCITY = 600;
/**
* 表示滾動到下一張圖片這個動作
*/
private static final int SCROLL_NEXT = 0;
/**
* 表示滾動到上一張圖片這個動作
*/
private static final int SCROLL_PREVIOUS = 1;
/**
* 表示滾動回原圖片這個動作
*/
private static final int SCROLL_BACK = 2;
private static Handler handler = new Handler();
/**
* 控制元件寬度
*/
public static int mWidth;
private VelocityTracker mVelocityTracker;
private Scroller mScroller;
/**
* 圖片滾動監聽器,當圖片發生滾動時回撥這個介面
*/
private OnImageSwitchListener mListener;
/**
* 記錄當前的觸控狀態
*/
private int mTouchState = TOUCH_STATE_REST;
/**
* 記錄被判定為滾動運動的最小滾動值
*/
private int mTouchSlop;
/**
* 記錄控制元件高度
*/
private int mHeight;
/**
* 記錄每張圖片的寬度
*/
private int mImageWidth;
/**
* 記錄圖片的總數量
*/
private int mCount;
/**
* 記錄當前顯示圖片的座標
*/
private int mCurrentImage;
/**
* 記錄上次觸控的橫座標值
*/
private float mLastMotionX;
/**
* 是否強制重新佈局
*/
private boolean forceToRelayout;
private int[] mItems;
public Image3DSwitchView(Context context, AttributeSet attrs) {
super(context, attrs);
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
mScroller = new Scroller(context);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (changed || forceToRelayout) {
mCount = getChildCount();
// 圖片數量必須大於5,不然無法正常顯示
if (mCount < 5) {
return;
}
mWidth = getMeasuredWidth();
mHeight = getMeasuredHeight();
// 每張圖片的寬度設定為控制元件寬度的百分之六十
mImageWidth = (int) (mWidth * 0.6);
if (mCurrentImage >= 0 && mCurrentImage < mCount) {
mScroller.abortAnimation();
setScrollX(0);
int left = -mImageWidth * 2 + (mWidth - mImageWidth) / 2;
// 分別獲取每個位置上應該顯示的圖片下標
int[] items = { getIndexForItem(1), getIndexForItem(2),
getIndexForItem(3), getIndexForItem(4),
getIndexForItem(5) };
mItems = items;
// 通過迴圈為每張圖片設定位置
for (int i = 0; i < items.length; i++) {
Image3DView childView = (Image3DView) getChildAt(items[i]);
childView.layout(left + IMAGE_PADDING, 0, left
+ mImageWidth - IMAGE_PADDING, mHeight);
childView.initImageViewBitmap();
left = left + mImageWidth;
}
refreshImageShowing();
}
forceToRelayout = false;
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (mScroller.isFinished()) {
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(event);
int action = event.getAction();
float x = event.getX();
switch (action) {
case MotionEvent.ACTION_DOWN:
// 記錄按下時的橫座標
mLastMotionX = x;
break;
case MotionEvent.ACTION_MOVE:
int disX = (int) (mLastMotionX - x);
mLastMotionX = x;
scrollBy(disX, 0);
// 當發生移動時重新整理圖片的顯示狀態
refreshImageShowing();
break;
case MotionEvent.ACTION_UP:
mVelocityTracker.computeCurrentVelocity(1000);
int velocityX = (int) mVelocityTracker.getXVelocity();
if (shouldScrollToNext(velocityX)) {
// 滾動到下一張圖
scrollToNext();
} else if (shouldScrollToPrevious(velocityX)) {
// 滾動到上一張圖
scrollToPrevious();
} else {
// 滾動回當前圖片
scrollBack();
}
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
break;
}
}
return true;
}
/**
* 根據當前的觸控狀態來決定是否遮蔽子控制元件的互動能力。
*/
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
int action = ev.getAction();
if ((action == MotionEvent.ACTION_MOVE)
&& (mTouchState != TOUCH_STATE_REST)) {
return true;
}
float x = ev.getX();
switch (action) {
case MotionEvent.ACTION_DOWN:
mLastMotionX = x;
mTouchState = TOUCH_STATE_REST;
break;
case MotionEvent.ACTION_MOVE:
int xDiff = (int) Math.abs(mLastMotionX - x);
if (xDiff > mTouchSlop) {
mTouchState = TOUCH_STATE_SCROLLING;
}
break;
case MotionEvent.ACTION_UP:
default:
mTouchState = TOUCH_STATE_REST;
break;
}
return mTouchState != TOUCH_STATE_REST;
}
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
refreshImageShowing();
postInvalidate();
}
}
/**
* 設定圖片滾動的監聽器,每當有圖片滾動時會回撥此介面。
*
* @param listener
* 圖片滾動監聽器
*/
public void setOnImageSwitchListener(OnImageSwitchListener listener) {
mListener = listener;
}
/**
* 設定當前顯示圖片的下標,注意如果該值小於零或大於等於圖片的總數量,圖片則無法正常顯示。
*
* @param currentImage
* 圖片的下標
*/
public void setCurrentImage(int currentImage) {
mCurrentImage = currentImage;
requestLayout();
}
/**
* 滾動到下一張圖片。
*/
public void scrollToNext() {
if (mScroller.isFinished()) {
int disX = mImageWidth - getScrollX();
checkImageSwitchBorder(SCROLL_NEXT);
if (mListener != null) {
mListener.onImageSwitch(mCurrentImage);
}
beginScroll(getScrollX(), 0, disX, 0, SCROLL_NEXT);
}
}
/**
* 滾動到上一張圖片。
*/
public void scrollToPrevious() {
if (mScroller.isFinished()) {
int disX = -mImageWidth - getScrollX();
checkImageSwitchBorder(SCROLL_PREVIOUS);
if (mListener != null) {
mListener.onImageSwitch(mCurrentImage);
}
beginScroll(getScrollX(), 0, disX, 0, SCROLL_PREVIOUS);
}
}
/**
* 滾動回原圖片。
*/
public void scrollBack() {
if (mScroller.isFinished()) {
beginScroll(getScrollX(), 0, -getScrollX(), 0, SCROLL_BACK);
}
}
/**
* 回收所有圖片物件,釋放記憶體。
*/
public void clear() {
for (int i = 0; i < mCount; i++) {
Image3DView childView = (Image3DView) getChildAt(i);
childView.recycleBitmap();
}
}
/**
* 讓控制元件中的所有圖片開始滾動。
*/
private void beginScroll(int startX, int startY, int dx, int dy,
final int action) {
int duration = (int) (700f / mImageWidth * Math.abs(dx));
mScroller.startScroll(startX, startY, dx, dy, duration);
invalidate();
handler.postDelayed(new Runnable() {
@Override
public void run() {
if (action == SCROLL_NEXT || action == SCROLL_PREVIOUS) {
forceToRelayout = true;
requestLayout();
}
}
}, duration);
}
/**
* 根據當前圖片的下標和傳入的item引數,來判斷item位置上應該顯示哪張圖片。
*
* @param item
* 取值範圍是1-5
* @return 對應item位置上應該顯示哪張圖片。
*/
private int getIndexForItem(int item) {
int index = -1;
index = mCurrentImage + item - 3;
while (index < 0) {
index = index + mCount;
}
while (index > mCount - 1) {
index = index - mCount;
}
return index;
}
/**
* 重新整理所有圖片的顯示狀態,包括當前的旋轉角度。
*/
private void refreshImageShowing() {
for (int i = 0; i < mItems.length; i++) {
Image3DView childView = (Image3DView) getChildAt(mItems[i]);
childView.setRotateData(i, getScrollX());
childView.invalidate();
}
}
/**
* 檢查圖片的邊界,防止圖片的下標超出規定範圍。
*/
private void checkImageSwitchBorder(int action) {
if (action == SCROLL_NEXT && ++mCurrentImage >= mCount) {
mCurrentImage = 0;
} else if (action == SCROLL_PREVIOUS && --mCurrentImage < 0) {
mCurrentImage = mCount - 1;
}
}
/**
* 判斷是否應該滾動到下一張圖片。
*/
private boolean shouldScrollToNext(int velocityX) {
return velocityX < -SNAP_VELOCITY || getScrollX() > mImageWidth / 2;
}
/**
* 判斷是否應該滾動到上一張圖片。
*/
private boolean shouldScrollToPrevious(int velocityX) {
return velocityX > SNAP_VELOCITY || getScrollX() < -mImageWidth / 2;
}
/**
* 圖片滾動的監聽器
*/
public interface OnImageSwitchListener {
/**
* 當圖片滾動時會回撥此方法
*
* @param currentImage
* 當前圖片的座標
*/
void onImageSwitch(int currentImage);
}
}
這段程式碼也比較長,我們來一點點進行分析。在onLayout()方法首先要判斷子檢視個數是不是大於等於5,如果不足5個則圖片輪播器無法正常顯示,直接return掉。如果大於等於5個,就會通過一個for迴圈來為每個子檢視分配顯示的位置,而每個子檢視都是一個Image3DView,在for迴圈中又會呼叫Image3DView的initImageViewBitmap()方法來為每個控制元件執行初始化操作,之後會呼叫refreshImageShowing()方法來重新整理圖片的顯示狀態。
接著當手指在Image3DSwitchView控制元件上滑動的時候就會進入到onTouchEvent()方法中,當手指按下時會記錄按下時的橫座標,然後當手指滑動時會計算出滑動的距離,並呼叫scrollBy()方法來進行滾動,當手指離開螢幕時會距離當前滑動的距離和速度來決定,是滾動到下一張圖片,還是滾動到上一張圖片,還是滾動回原圖片。分別呼叫的方法是scrollToNext()、scrollToPrevious()和scrollBack()。
在scrollToNext()方法中會先計算一下還需滾動的距離,然後進行一下邊界檢查,防止當前圖片的下標超出合理範圍,接著會呼叫beginScroll()方法來進行滾動。在beginScroll()方法中其實就是呼叫了Scroller的startScroll()方法來執行滾動操作的,當滾動結束後還會呼叫requestLayout()方法來要求重新佈局,之後onLayout()方法就會重新執行,每個圖片的位置也就會跟著改變了。至於scrollToPrevious()和scrollBack()方法的原理也是一樣的,這裡就不再重複分析了。
那麼在onLayout()方法的最後呼叫的refreshImageShowing()方法到底執行了什麼操作呢?其實就是遍歷了一下每個Image3DView控制元件,然後呼叫它的setRotateData()方法,並把圖片的下標和滾動距離傳進去,這樣每張圖片就知道應該如何進行旋轉了。
另外一些其它的細節就不在這裡講解了,註釋寫的還是比較詳細的,你可以慢慢地去分析和理解。
那麼下面我們來看下如何使用Image3DSwitchView這個控制元件吧,開啟或新建activity_main.xml作為程式的主佈局檔案,程式碼如下所示:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#fff" >
<com.example.imageswitchviewtest.Image3DSwitchView
android:id="@+id/image_switch_view"
android:layout_width="match_parent"
android:layout_height="150dp" >
<com.example.imageswitchviewtest.Image3DView
android:id="@+id/image1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
android:src="@drawable/image1" />
<com.example.imageswitchviewtest.Image3DView
android:id="@+id/image2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
android:src="@drawable/image2" />
<com.example.imageswitchviewtest.Image3DView
android:id="@+id/image3"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
android:src="@drawable/image3" />
<com.example.imageswitchviewtest.Image3DView
android:id="@+id/image4"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
android:src="@drawable/image4" />
<com.example.imageswitchviewtest.Image3DView
android:id="@+id/image5"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
android:src="@drawable/image5" />
<com.example.imageswitchviewtest.Image3DView
android:id="@+id/image6"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
android:src="@drawable/image6" />
<com.example.imageswitchviewtest.Image3DView
android:id="@+id/image7"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
android:src="@drawable/image7" />
</com.example.imageswitchviewtest.Image3DSwitchView>
</RelativeLayout>
可以看到,這裡我們引入了一個Image3DSwitchView控制元件,然後在這個控制元件下面又添加了7個Image3DView控制元件,每個Image3DView其實就是一個ImageView,因此我們可以通過android:src屬於給它指定一張圖片。注意前面也說過了,Image3DSwitchView控制元件下的子控制元件必須大於等於5個,不然將無法正常顯示。
程式碼到這裡就寫得差不多了,現在執行一下程式就可以看到一個3D版的圖片輪播器,使用手指進行滑動可以檢視更多的圖片,如下圖所示:
怎麼樣?效果還是非常不錯的吧!除此之外,Image3DSwitchView中還提供了setCurrentImage()方法和setOnImageSwitchListener()方法,分別可用於設定當前顯示哪張圖片,以及設定圖片滾動的監聽器,有了這些方法,你可以更加輕鬆地在Image3DSwitchView的基礎上進行擴充套件,比如說加入頁籤顯示功能等。
好了,今天的講解就到這裡,有疑問的朋友可以在下面留言(不過最近工作著實繁忙,恐怕無法一一回復大家)。
關注我的技術公眾號,每天都有優質技術文章推送。關注我的娛樂公眾號,工作、學習累了的時候放鬆一下自己。
微信掃一掃下方二維碼即可關注:
相關推薦
Android高階圖片滾動控制元件,編寫3D版的圖片輪播器
大家好,好久不見了,最近由於工作特別繁忙,已經有一個多月的時間沒寫部落格了,我也是深感慚愧。那麼今天的這篇既然是闊別了一個多月的文章,當然要帶來更加給力點的內容了,那麼話不多說,趕快進入到今天的正題吧。說到圖片輪播器,很多的Android應用中都會帶有這個功能,比如說網易新聞
Android高階圖片滾動控制元件,編寫3D版的圖片輪播器 一屏顯示多個圖片
大家好,好久不見了,最近由於工作特別繁忙,已經有一個多月的時間沒寫部落格了,我也是深感慚愧。那麼今天的這篇既然是闊別了一個多月的文章,當然要帶來更加給力點的內容了,那麼話不多說,趕快進入到今天的正題吧。 說到圖片輪播器,很多的Android應用中都會帶有這個
Android 層疊卡片控制元件,仿"探探app"
StackLayout 簡介:Android 層疊卡片控制元件,防"探探 app" 1.支援自定義卡片的堆疊效果 2.支援自定義卡片移除動畫 3.支援載入更多 更多:作者 提 Bug 標籤: android-stack-card-swipe- 功能
Android 自定義RatingBar控制元件,顯示不全問題
最近專案要用到 自定義RatingBar控制元件 但是自定義好了樣式發現,星星只顯示一半,於是在網上找各種解決方法。 最後竟然是直接把資源圖片,移動到較高解析度的資料夾裡面。 我的解決做法如下,如果有更好的方法,請留言告知。 一,先寫一個drawable,設定好backg
Android有沒有什麼控制元件,支援選擇一段時間
不要日期,只要整點之間的時間段選擇, 例如選:08:00 - 10:00、 13:00 - 17:00等。 請問有沒有這種元件,或者其他work around思路也行。 下面是我的一些搜尋: 1. android 自帶的TimePicker, 只能選擇單個時間點,不
Android開發之AutoCompleteTextView控制元件,自動提示
AutoCompleteTextView是一個提供了聯想詞的控制元件,可以看做是EditText的升級版本 佈局: <AutoCompleteTextView android:
Android banner,輪播圖自動滾動控制元件
簡介 現在的絕大數app都有banner介面,實現迴圈播放多個廣告圖片和手動滑動迴圈等功能。因為ViewPager並不支援迴圈翻頁, 所以要實現迴圈還得需要自己去動手,我就把專案中的控制元件剔了出來,希望大家覺得有用。目前框架可以進行不同樣式、不同動畫設定,
Android仿微信朋友圈九宮格圖片展示自定義控制元件,支援縮放動畫~
一直對微信朋友圈九宮格圖片顯示控制元件比較好奇,找到一篇介紹相關騷操作的部落格 部落格雖好但是不夠完美,缺少點選圖片預覽頁面和縮放動畫,作為一個不斷追求完美主義的人,我想把這個控制元件結合到專案中而不是單純作為一個控制元件。 下面是我的實現效果圖: (
android studio引入一個自定義的佈局,自定義控制元件,避免每一個活動中都編寫一樣佈局程式碼的問題
本次演示的是標題欄上建立按鈕,即 引入自定義佈局和自定義控制元件的應用十分的廣泛,它的形成的效果很多的應用程式都有,我們可以自定義標題欄,因為普通的標題欄就是一行文字,但是,我們可以發現,很多手機軟體的標題欄上都有返回,或者 進入的按鈕,尤其是全面屏的手機。而且它還能解
Android自定義View,高仿QQ音樂歌詞滾動控制元件!
最近在以QQ音樂為樣板做一個手機音樂播放器,原始碼下篇博文放出。今天我想聊的是這個QQ音樂播放器中歌詞顯示控制元件的問題,和小夥伴們一起來探討怎麼實現這個歌詞滾動的效果。OK,廢話不多說,先來看看效果圖:好,接下來我們就來看看怎麼實現這樣一個效果。本文主要包括如下幾方面內容:
Android Studio中ListView控制元件:圖片+文字,文字顯示不出來
今天在學習 ListView 控制元件的時候,想做一個滾動介面,顯示圖片+文字。 想象的是這樣子的: 沒想到執行的時候是這樣子的: 於是我抱著試一試的心態壓縮了一下圖片:結果就按照想的顯示出來了。 我覺得應該還有其他的解決辦法,歡迎指出。
Android中使用ImageView控制元件顯示網路圖片
在android4.0以後的版本中,為了使得主介面流暢,所以設定了不允許在主執行緒中訪問網路,為了安全,又不允許在其它執行緒中訪問控制元件,這樣就造成了ImageView等需要使用網路的控制元件更新時的問題,本文以Handler+Runnable的方式實現了ImageView控制元件顯示網路圖片.
android 禁止scrollview 因控制元件變化自動滾動到底的方法
網上的說法是焦點問題導致,新焦點在下面時,檢視會滾動使焦點可見。但網上提的在scrollview裡層的LinearLayout裡新增 android:focusable="true" android:focusableIn
iOS開發圖片加標題滾動控制元件封裝
說明 · 使用UICollectionView實現,封裝在 HorizontalSlipMenuView 中,使用時只要例項化 HorizontalSlipMenuView 並設定資料來源後,新增到父檢視上即可 Demo地址:https://github.com/liujunwei
Android常用控制元件,用執行緒寫一個進度條。
一、事件監聽(三種寫法) 1、標籤上直接繫結監聽方法 public void xxx(View view) 2、 建立監聽器物件,元件再繫結監聽器物件 2.1、匿名內部類 2.2、使用匿名內部類並定義成全域性的屬性 二、文字框(TextView) 1、T
Android Studio的基本控制元件 圖片框與進度條
今日重點: 1. Toast(吐絲框); 2. ImageView; 3. ProgressBar (進度條); 4. 如何讓子執行緒成功連線主執行緒(重點)。 技術分點 Toast(吐絲框); 1.1 Toast是Android
android自定義星級評分控制元件,可實現只顯示實心星星
話不多說,上圖 近日app需求弄一個等級展示,看了下UI圖,只顯示實星(點亮的星星).如圖 但是網上關於星級評分的例子大多這樣 也展示虛心星星 通過自定義View package com.starsbar; import android.content.C
Android仿百度,高德地圖位置交換控制元件,水平or垂直交換控制元件位置
開始看到這個需求,準備使用檢視動畫 TranslateAnimation 來實現。但是把因為檢視動畫只是移動了檢視,控制元件的位置沒有改變,導致只能執行一次交換的動作,不能交換回來。逐放棄改用屬性動畫來實現。直接交換兩個控制元件的位置,交換後在將原來控制元件的值賦予交換後
一個Android文字輪播控制元件,實現了可垂直跑、可水平跑的跑馬燈
Android文字輪播控制元件 現在的絕大數APP特別是類似淘寶京東等這些大型APP都有文字輪播介面,實現迴圈輪播多個廣告詞等功能;這種控制元件俗稱“跑馬燈”,而TextBannerView已經實現了可垂直跑、可水平跑的跑馬燈了。 效果圖 Attribute
Android開發技巧——定製仿微信圖片裁剪控制元件
拍照——裁剪,或者是選擇圖片——裁剪,是我們設定頭像或上傳圖片時經常需要的一組操作。上篇講了Camera的使用,這篇講一下我對圖片裁剪的實現。 背景 下面的需求都來自產品。 裁剪圖片要像微信那樣,拖動和放大的是圖片,裁剪框不動。 裁剪框外的內容要有半透