Android實(shí)現(xiàn)3D層疊式卡片圖片展示
本文實(shí)例為大家分享了Android實(shí)現(xiàn)3D層疊式卡片圖片展示的具體代碼,供大家參考,具體內(nèi)容如下
先看效果
好了效果看了,感興趣的往下看哦!
整體實(shí)現(xiàn)思路
1、重寫(xiě)RelativeLayout 實(shí)現(xiàn) 鎖定寬高比例的 RelativeLayout
2、自定義一個(gè)支持滑動(dòng)的面板 繼承 ViewGroup
3、卡片View繪制
4、頁(yè)面中使用布局
首先為了更好的展示圖片我們重寫(xiě)一下 RelativeLayout 編寫(xiě)一個(gè)鎖定寬高比例的 RelativeLayout
AutoScaleRelativeLayout
public class AutoScaleRelativeLayout extends RelativeLayout { //寬高比例 private float widthHeightRate = 0.35f; public AutoScaleRelativeLayout(Context context) { this(context, null); } public AutoScaleRelativeLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public AutoScaleRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); //通過(guò)布局獲取寬高比例 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.card, 0, 0); widthHeightRate = a.getFloat(R.styleable.card_widthHeightRate, widthHeightRate); a.recycle(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); // 調(diào)整高度 int width = getMeasuredWidth(); int height = (int) (width * widthHeightRate); ViewGroup.LayoutParams lp = getLayoutParams(); lp.height = height; setLayoutParams(lp); } }
這樣我們就編寫(xiě)好了我們想要的父布局
使用方法
<com.petterp.toos.ImageCard.AutoScaleRelativeLayout android:id="@+id/card_top_layout" android:layout_width="match_parent" android:layout_height="wrap_content" card:widthHeightRate="0.6588"> <!-- widthHeightRate:就是設(shè)置寬高的百分比--> <ImageView android:id="@+id/card_image_view" android:layout_width="fill_parent" android:layout_height="match_parent" android:scaleType="fitXY" /> <!-- 這是我們展示的圖片--> <View android:id="@+id/maskView" android:layout_width="fill_parent" android:layout_height="match_parent" android:background="?android:attr/selectableItemBackground" android:clickable="true" /> <!-- 這個(gè)是為了讓我們圖片上有波紋--> </com.petterp.toos.ImageCard.AutoScaleRelativeLayout>
接下來(lái)就是主要布局,也就是展示圖片的布局了
為了實(shí)現(xiàn)滑動(dòng)我們編寫(xiě)一個(gè)支持滑動(dòng)的畫(huà)板
//事件處理 @Override public boolean dispatchTouchEvent(MotionEvent ev) { int action = ev.getActionMasked(); // 按下時(shí)保存坐標(biāo)信息 if (action == MotionEvent.ACTION_DOWN) { this.downPoint.x = (int) ev.getX(); this.downPoint.y = (int) ev.getY(); } return super.dispatchTouchEvent(ev); } /* touch事件的攔截與處理都交給mDraghelper來(lái)處理 */ @Override public boolean onInterceptTouchEvent(MotionEvent ev) { boolean shouldIntercept = mDragHelper.shouldInterceptTouchEvent(ev); boolean moveFlag = moveDetector.onTouchEvent(ev); int action = ev.getActionMasked(); if (action == MotionEvent.ACTION_DOWN) { // ACTION_DOWN的時(shí)候就對(duì)view重新排序 if (mDragHelper.getViewDragState() == ViewDragHelper.STATE_SETTLING) { mDragHelper.abort(); } orderViewStack(); // 保存初次按下時(shí)arrowFlagView的Y坐標(biāo) // action_down時(shí)就讓mDragHelper開(kāi)始工作,否則有時(shí)候?qū)е庐惓? mDragHelper.processTouchEvent(ev); } return shouldIntercept && moveFlag; } @Override public boolean onTouchEvent(MotionEvent e) { try { // 統(tǒng)一交給mDragHelper處理,由DragHelperCallback實(shí)現(xiàn)拖動(dòng)效果 // 該行代碼可能會(huì)拋異常,正式發(fā)布時(shí)請(qǐng)將這行代碼加上try catch mDragHelper.processTouchEvent(e); } catch (Exception ex) { ex.printStackTrace(); } return true; } //計(jì)算 @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { measureChildren(widthMeasureSpec, heightMeasureSpec); int maxWidth = MeasureSpec.getSize(widthMeasureSpec); int maxHeight = MeasureSpec.getSize(heightMeasureSpec); setMeasuredDimension( resolveSizeAndState(maxWidth, widthMeasureSpec, 0), resolveSizeAndState(maxHeight, heightMeasureSpec, 0)); allWidth = getMeasuredWidth(); allHeight = getMeasuredHeight(); } //定位 @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { // 布局卡片view int size = viewList.size(); for (int i = 0; i < size; i++) { View viewItem = viewList.get(i); int childHeight = viewItem.getMeasuredHeight(); int viewLeft = (getWidth() - viewItem.getMeasuredWidth()) / 2; viewItem.layout(viewLeft, itemMarginTop, viewLeft + viewItem.getMeasuredWidth(), itemMarginTop + childHeight); int offset = yOffsetStep * i; float scale = 1 - SCALE_STEP * i; if (i > 2) { // 備用的view offset = yOffsetStep * 2; scale = 1 - SCALE_STEP * 2; } viewItem.offsetTopAndBottom(offset); viewItem.setScaleX(scale); viewItem.setScaleY(scale); } // 布局底部按鈕的View if (null != bottomLayout) { int layoutTop = viewList.get(0).getBottom() + bottomMarginTop; bottomLayout.layout(left, layoutTop, right, layoutTop + bottomLayout.getMeasuredHeight()); } // 初始化一些中間參數(shù) initCenterViewX = viewList.get(0).getLeft(); initCenterViewY = viewList.get(0).getTop(); childWith = viewList.get(0).getMeasuredWidth(); } //onFinishInflate 當(dāng)View中所有的子控件均被映射成xml后觸發(fā) @Override protected void onFinishInflate() { super.onFinishInflate(); // 渲染完成,初始化卡片view列表 viewList.clear(); int num = getChildCount(); for (int i = num - 1; i >= 0; i--) { View childView = getChildAt(i); if (childView.getId() == R.id.card_bottom_layout) { bottomLayout = childView; initBottomLayout(); } else { // for循環(huán)取view的時(shí)候,是從外層往里取 CardItemView viewItem = (CardItemView) childView; viewItem.setParentView(this); viewItem.setTag(i + 1); viewItem.maskView.setOnClickListener(btnListener); viewList.add(viewItem); } } CardItemView bottomCardView = viewList.get(viewList.size() - 1); bottomCardView.setAlpha(0); }
卡片View繪制
private void initSpring() { SpringConfig springConfig = SpringConfig.fromBouncinessAndSpeed(15, 20); SpringSystem mSpringSystem = SpringSystem.create(); springX = mSpringSystem.createSpring().setSpringConfig(springConfig); springY = mSpringSystem.createSpring().setSpringConfig(springConfig); springX.addListener(new SimpleSpringListener() { @Override public void onSpringUpdate(Spring spring) { int xPos = (int) spring.getCurrentValue(); setScreenX(xPos); parentView.onViewPosChanged(CardItemView.this); } }); springY.addListener(new SimpleSpringListener() { @Override public void onSpringUpdate(Spring spring) { int yPos = (int) spring.getCurrentValue(); setScreenY(yPos); parentView.onViewPosChanged(CardItemView.this); } }); } //裝載數(shù)據(jù) public void fillData(CardDataItem itemData) { Glide.with(getContext()).load(itemData.imagePath).into(imageView); } /** * 動(dòng)畫(huà)移動(dòng)到某個(gè)位置 */ public void animTo(int xPos, int yPos) { setCurrentSpringPos(getLeft(), getTop()); springX.setEndValue(xPos); springY.setEndValue(yPos); } /** * 設(shè)置當(dāng)前spring位置 */ private void setCurrentSpringPos(int xPos, int yPos) { springX.setCurrentValue(xPos); springY.setCurrentValue(yPos); }
接下來(lái)我們需要使用它 編寫(xiě)Fragment布局
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" xmlns:card="http://schemas.android.com/apk/res-auto" android:background="#fff" android:orientation="vertical"> <com.petterp.toos.ImageCard.CardSlidePanel android:id="@+id/image_slide_panel" android:layout_width="match_parent" android:layout_height="match_parent" card:bottomMarginTop="38dp" card:itemMarginTop="10dp" card:yOffsetStep="26dp"> <LinearLayout android:id="@+id/card_bottom_layout" android:layout_width="fill_parent" android:layout_height="wrap_content" android:gravity="center" android:orientation="horizontal"> <Button android:background="#03A9F4" android:text="右側(cè)移除" android:id="@+id/card_left_btn" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <Button android:background="#03A9F4" android:text="右側(cè)移除" android:id="@+id/card_right_btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="10dp" /> </LinearLayout> <com.petterp.toos.ImageCard.CardItemView android:layout_width="match_parent" android:layout_height="wrap_content" /> <com.petterp.toos.ImageCard.CardItemView android:layout_width="match_parent" android:layout_height="wrap_content" /> <com.petterp.toos.ImageCard.CardItemView android:layout_width="match_parent" android:layout_height="wrap_content" /> <com.petterp.toos.ImageCard.CardItemView android:layout_width="match_parent" android:layout_height="wrap_content" /> </com.petterp.toos.ImageCard.CardSlidePanel> </LinearLayout>
代碼中的使用
private void initView(View rootView) { CardSlidePanel slidePanel = (CardSlidePanel) rootView .findViewById(R.id.image_slide_panel); cardSwitchListener = new CardSlidePanel.CardSwitchListener() { @Override public void onShow(int index) { Toast.makeText(getContext(), "CardFragment"+"正在顯示=" +index, Toast.LENGTH_SHORT).show(); } //type 0=右邊 ,-1=左邊 @Override public void onCardVanish(int index, int type) { Toast.makeText(getContext(), "CardFragment"+ "正在消失=" + index + " 消失type=" + type, Toast.LENGTH_SHORT).show(); } @Override public void onItemClick(View cardView, int index) { Toast.makeText(getContext(), "CardFragment"+"卡片點(diǎn)擊=" + index, Toast.LENGTH_SHORT).show(); } }; slidePanel.setCardSwitchListener(cardSwitchListener); prepareDataList(); slidePanel.fillData(dataList); } //封裝數(shù)據(jù) private void prepareDataList() { int num = imagePaths.length; //重復(fù)添加數(shù)據(jù)10次(測(cè)試數(shù)據(jù)太少) for (int j = 0; j < 10; j++) { for (int i = 0; i < num; i++) { CardDataItem dataItem = new CardDataItem(); dataItem.imagePath = imagePaths[i]; dataList.add(dataItem); } } }
到此主要邏輯代碼就編寫(xiě)完了
詳細(xì)說(shuō)明代碼中已經(jīng)注釋 ,全部代碼請(qǐng)看源碼
源碼:github源碼
源碼中的TestCardFragment 為使用模板
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android開(kāi)發(fā)之Sqliteopenhelper用法實(shí)例分析
這篇文章主要介紹了Android開(kāi)發(fā)之Sqliteopenhelper用法,實(shí)例分析了SQLiteOpenHelper類(lèi)操作數(shù)據(jù)庫(kù)的相關(guān)技巧,需要的朋友可以參考下2015-05-05Android中AnimationDrawable使用的簡(jiǎn)單實(shí)例
這篇文章介紹了Android中AnimationDrawable使用的簡(jiǎn)單實(shí)例,有需要的朋友可以參考一下2013-10-10Android開(kāi)發(fā)之拖動(dòng)條/滑動(dòng)條控件、星級(jí)評(píng)分控件功能的實(shí)例代碼
這篇文章主要介紹了Android開(kāi)發(fā)之拖動(dòng)條/滑動(dòng)條控件、星級(jí)評(píng)分控件功能的實(shí)例代碼,需要的朋友可以參考下2019-05-05Android 使用Shell腳本截屏并自動(dòng)傳到電腦上
這篇文章主要介紹了Android 使用Shell腳本截屏并自動(dòng)傳到電腦上的相關(guān)資料,需要的朋友可以參考下2016-02-02Android中實(shí)現(xiàn)為T(mén)extView添加多個(gè)可點(diǎn)擊的文本
這篇文章主要介紹了Android中實(shí)現(xiàn)為T(mén)extView添加多個(gè)可點(diǎn)擊的文本,可實(shí)現(xiàn)類(lèi)似Android社交軟件顯示點(diǎn)贊用戶(hù)并通過(guò)用戶(hù)名稱(chēng)進(jìn)入該用戶(hù)主頁(yè)的功能,是非常實(shí)用的技巧,需要的朋友可以參考下2014-09-09Android實(shí)現(xiàn)短信驗(yàn)證碼獲取自動(dòng)填寫(xiě)功能(詳細(xì)版)
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)短信驗(yàn)證碼獲取自動(dòng)填寫(xiě)功能,很實(shí)用的功能分享給大家,感興趣的小伙伴們可以參考一下2016-08-08Android App更改應(yīng)用的圖標(biāo)的實(shí)現(xiàn)方法
這篇文章主要介紹了Android App更改應(yīng)用的圖標(biāo)的實(shí)現(xiàn)方法的相關(guān)資料,主要是通過(guò)入口Activity android:icon="@drawable/new_app_icon" 指向新的應(yīng)用圖標(biāo),需要的朋友可以參考下2017-08-08Android實(shí)現(xiàn)聯(lián)動(dòng)下拉框 下拉列表spinner的實(shí)例代碼
這篇文章介紹了Android實(shí)現(xiàn)聯(lián)動(dòng)下拉框 下拉列表spinner的實(shí)例代碼,有需要的朋友可以參考一下2013-10-10Android ListView實(shí)現(xiàn)無(wú)限循環(huán)滾動(dòng)
這篇文章主要為大家詳細(xì)介紹了Android ListView實(shí)現(xiàn)無(wú)限循環(huán)滾動(dòng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-06-06