├── .gitignore ├── .idea ├── .name ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── gradle.xml ├── misc.xml ├── modules.xml └── runConfigurations.xml ├── README.md ├── SwipeCardLayout.apk ├── app ├── .gitignore ├── build.gradle ├── libs │ └── KLog-1.3.0.jar ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── alex │ │ └── swipecardlayout │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ ├── com │ │ │ └── alex │ │ │ │ └── swipecardlayout │ │ │ │ └── MainActivity.java │ │ └── github │ │ │ └── alex │ │ │ └── swipecardlayout │ │ │ └── SwipeCardLayout.java │ └── res │ │ ├── drawable │ │ ├── bg9_normal.9.png │ │ ├── bg9_selected.9.png │ │ ├── selected_left.xml │ │ ├── selected_right.xml │ │ └── selected_round.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── card_user_info.xml │ │ └── content_main.xml │ │ ├── menu │ │ └── menu_main.xml │ │ ├── mipmap-hdpi │ │ ├── ic_index_foot_2_normal.png │ │ ├── ic_index_foot_2_selected.png │ │ ├── ic_index_foot_4_normal.png │ │ ├── ic_index_foot_4_selected.png │ │ ├── ic_launcher.png │ │ ├── logo_circle.png │ │ └── logo_meinv.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-v21 │ │ └── styles.xml │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── alex │ └── swipecardlayout │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | SwipeCardLayout -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 23 | 24 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SwipeCardLayout 2 | 仿大街网-堆栈式卡片布局 3 | 4 | > 博客地址 http://www.jianshu.com/p/1a9871f7fee6 5 | -------------------------------------------------------------------------------- /SwipeCardLayout.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alex-Cin/SwipeCardLayout/fa3859bbc739e6496381f625f3211b264fcfd26b/SwipeCardLayout.apk -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "24.0.0 rc2" 6 | 7 | defaultConfig { 8 | applicationId "com.alex.swipecardlayout" 9 | minSdkVersion 15 10 | targetSdkVersion 23 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(include: ['*.jar'], dir: 'libs') 24 | testCompile 'junit:junit:4.12' 25 | compile 'com.android.support:appcompat-v7:23.2.1' 26 | compile files('libs/KLog-1.3.0.jar') 27 | } 28 | -------------------------------------------------------------------------------- /app/libs/KLog-1.3.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alex-Cin/SwipeCardLayout/fa3859bbc739e6496381f625f3211b264fcfd26b/app/libs/KLog-1.3.0.jar -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in E:\Android\sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/alex/swipecardlayout/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.alex.swipecardlayout; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/java/com/alex/swipecardlayout/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.alex.swipecardlayout; 2 | 3 | import android.annotation.TargetApi; 4 | import android.content.Context; 5 | import android.os.Build; 6 | import android.os.Bundle; 7 | import android.support.v7.app.AppCompatActivity; 8 | import android.view.View; 9 | import android.widget.TextView; 10 | 11 | import com.socks.library.KLog; 12 | 13 | import github.alex.swipecardlayout.SwipeCardLayout; 14 | 15 | public class MainActivity extends AppCompatActivity { 16 | 17 | private SwipeCardLayout swipeCardLayout; 18 | private Context context; 19 | 20 | @Override 21 | protected void onCreate(Bundle savedInstanceState) { 22 | super.onCreate(savedInstanceState); 23 | setContentView(R.layout.activity_main); 24 | context = this; 25 | initView(); 26 | } 27 | 28 | @TargetApi(Build.VERSION_CODES.HONEYCOMB) 29 | private void initView() { 30 | findViewById(R.id.bt_reload).setOnClickListener(new MyOnClickListener()); 31 | swipeCardLayout = (SwipeCardLayout) findViewById(R.id.scl); 32 | swipeCardLayout.setCardView(16, 5, R.layout.card_user_info, 32); 33 | swipeCardLayout.setSwipeDistance(96); 34 | swipeCardLayout.setOnSwipeCardListener(new MyOnSwipeCardListener()); 35 | View lastView = swipeCardLayout.getLastView(); 36 | TextView textView = (TextView) lastView.findViewById(R.id.tv_info); 37 | textView.setText("剩余卡片数量 = " + 16 + " 卡片总数 = " + 16 + " 堆栈的长度 = " + 5); 38 | 39 | } 40 | 41 | private final class MyOnSwipeCardListener implements SwipeCardLayout.OnSwipeCardListener { 42 | @Override 43 | public void onSwipeFinish(int cardBalanceCount, int cardCount, int stackLength) { 44 | // KLog.e("currIndex = "+currIndex); 45 | View lastView = swipeCardLayout.getLastView(); 46 | if (lastView != null) { 47 | TextView textView = (TextView) lastView.findViewById(R.id.tv_info); 48 | textView.setText("剩余卡片数量 = " + cardBalanceCount + " 卡片总数 = " + cardCount + " 堆栈的长度 = " + stackLength); 49 | } 50 | if (cardBalanceCount > 0) { 51 | 52 | } else if (cardBalanceCount == 0) { 53 | findViewById(R.id.bt_reload).setVisibility(View.VISIBLE); 54 | } 55 | findViewById(R.id.iv_foot_left).setSelected(false); 56 | findViewById(R.id.iv_foot_right).setSelected(false); 57 | } 58 | 59 | @Override 60 | public void onSwiping(int swipeStatus, int cardBalanceCount, int cardCount, int stackLength) { 61 | View preLastView = swipeCardLayout.getPreLastView(); 62 | if (preLastView != null) { 63 | TextView textView = (TextView) preLastView.findViewById(R.id.tv_info); 64 | textView.setText("剩余卡片数量 = " + (cardBalanceCount-1) + " 卡片总数 = " + cardCount + " 堆栈的长度 = " + stackLength); 65 | } 66 | if (swipeStatus == -1) { 67 | findViewById(R.id.iv_foot_left).setSelected(true); 68 | findViewById(R.id.iv_foot_right).setSelected(false); 69 | } else if (swipeStatus == 0) { 70 | findViewById(R.id.iv_foot_left).setSelected(false); 71 | findViewById(R.id.iv_foot_right).setSelected(false); 72 | } else if (swipeStatus == 1) { 73 | findViewById(R.id.iv_foot_left).setSelected(false); 74 | findViewById(R.id.iv_foot_right).setSelected(true); 75 | } 76 | } 77 | } 78 | 79 | private final class MyOnClickListener implements View.OnClickListener { 80 | @Override 81 | public void onClick(View v) { 82 | if (R.id.bt_reload == v.getId()) { 83 | swipeCardLayout.setCardView(14, 5, R.layout.card_user_info, 32); 84 | View lastView = swipeCardLayout.getLastView(); 85 | TextView textView = (TextView) lastView.findViewById(R.id.tv_info); 86 | textView.setText("剩余卡片数量 = " + 14 + " 卡片总数 = " + 14 + " 当前卡片在堆栈中的编号 = " + 5 + " 堆栈的长度 = " + 5); 87 | findViewById(R.id.bt_reload).setVisibility(View.GONE); 88 | } 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /app/src/main/java/github/alex/swipecardlayout/SwipeCardLayout.java: -------------------------------------------------------------------------------- 1 | package github.alex.swipecardlayout; 2 | 3 | import android.annotation.TargetApi; 4 | import android.content.Context; 5 | import android.os.Build; 6 | import android.os.Handler; 7 | import android.os.Message; 8 | import android.os.SystemClock; 9 | import android.util.AttributeSet; 10 | import android.util.DisplayMetrics; 11 | import android.view.Gravity; 12 | import android.view.LayoutInflater; 13 | import android.view.MotionEvent; 14 | import android.view.View; 15 | import android.view.ViewGroup; 16 | import android.view.WindowManager; 17 | import android.widget.FrameLayout; 18 | 19 | public class SwipeCardLayout extends FrameLayout { 20 | private int cardViewLayoutId; 21 | private float startRawX; 22 | private float startRawY; 23 | private int cardHeight; 24 | private int cardWidth; 25 | /** 26 | * CardView 左右晃动的 半径 radius = cardHeight x 2 27 | */ 28 | private float radius; 29 | /** 30 | * 左右 状态的临界值 31 | */ 32 | private int swipeDistance; 33 | private int cardLeftMargin = 0; 34 | private int cardRightMargin = 0; 35 | private int cardTopMargin = 0; 36 | private int screenWidth; 37 | private ReLayoutHandler reLayoutHandler; 38 | private ReMoveHandler reMoveHandler; 39 | private OnSwipeCardListener onSwipeCardListener; 40 | private static final int whatRemoving = 100; 41 | private static final int whatRemoved = 101; 42 | /** 43 | * 卡片的总数 44 | */ 45 | private int cardCount; 46 | /** 47 | * 卡片的剩余个数 48 | */ 49 | private int cardBalanceCount; 50 | /** 51 | * 堆栈的原始长度 52 | */ 53 | private int stackLength; 54 | /** 55 | * 手指在X轴上滑动的矢量 向量 56 | */ 57 | private int distanceX; 58 | /** 59 | * 正在操作的 卡片 60 | */ 61 | private View swipingCardView; 62 | 63 | public SwipeCardLayout(Context context, AttributeSet attrs) { 64 | super(context, attrs); 65 | } 66 | 67 | /** 68 | * 设置子布局的资源 id,必须满足 cardCount > stackLength 69 | * 70 | * @param cardCount 卡片的总数 71 | * @param stackLength 堆栈的长度 72 | * @param cardViewLayoutId 卡片布局的 id 73 | * @param cardTopMargin 单位 dp 74 | */ 75 | public void setCardView(int cardCount, int stackLength, int cardViewLayoutId, int cardTopMargin) { 76 | this.cardViewLayoutId = cardViewLayoutId; 77 | this.cardCount = cardCount; 78 | this.cardBalanceCount = cardCount; 79 | this.stackLength = stackLength; 80 | swipeDistance = (int) dp2Px(48); 81 | reLayoutHandler = new ReLayoutHandler(); 82 | reMoveHandler = new ReMoveHandler(); 83 | screenWidth = getScreenWidth(getContext()); 84 | cardLeftMargin = (int) dp2Px(32); 85 | cardRightMargin = (int) dp2Px(32); 86 | this.cardTopMargin = (int) dp2Px(cardTopMargin); 87 | addCardView(stackLength); 88 | View lastChildView = getLastView(); 89 | cardHeight = getViewHeight(lastChildView); 90 | cardWidth = screenWidth - cardLeftMargin - cardRightMargin; 91 | radius = cardHeight * 2; 92 | } 93 | 94 | /** 95 | * 给堆栈布局添加 CardView 96 | */ 97 | private void addCardView(int stackLength) { 98 | LayoutParams params = new LayoutParams(screenWidth - cardLeftMargin - cardRightMargin, LayoutParams.WRAP_CONTENT); 99 | params.gravity = Gravity.CENTER_HORIZONTAL; 100 | params.topMargin = this.cardTopMargin; 101 | for (int i = 0; (i < stackLength) && (i < cardBalanceCount); i++) { 102 | View cardView = LayoutInflater.from(getContext()).inflate(cardViewLayoutId, null); 103 | cardView.setLayoutParams(params); 104 | addView(cardView, i); 105 | } 106 | } 107 | 108 | /** 109 | * 设置水平滑动的临界值 110 | * 111 | * @param swipeDistance 单位 dp 112 | */ 113 | public void setSwipeDistance(int swipeDistance) { 114 | this.swipeDistance = (int) dp2Px(swipeDistance); 115 | } 116 | 117 | /** 118 | * 得到最后一个 ChildView 119 | */ 120 | public View getLastView() { 121 | int childCount = getChildCount(); 122 | if (childCount > 0) { 123 | return getChildAt(childCount - 1); 124 | } else { 125 | return null; 126 | } 127 | } 128 | 129 | /** 130 | * 得到倒数第2个 ChildView 131 | */ 132 | public View getPreLastView() { 133 | int childCount = getChildCount(); 134 | if (childCount > 1) { 135 | return getChildAt(childCount - 2); 136 | } else { 137 | return null; 138 | } 139 | } 140 | @Override 141 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 142 | super.onLayout(changed, left, top, right, bottom); 143 | View lastChildView = getLastView(); 144 | if (lastChildView != null) { 145 | lastChildView.setOnTouchListener(new CardOnTouchListener()); 146 | } 147 | int childCount = getChildCount(); 148 | for (int i = childCount - 1, j = 0; i >= 0; i--, j++) { 149 | View view = getChildAt(i); 150 | //view.setScaleX(Math.pow(0.9,j)); 151 | view.layout(cardLeftMargin + j * 20, getPaddingTop() + j * 20 + cardTopMargin, cardWidth + cardRightMargin - j * 20, cardHeight + getPaddingTop() + cardTopMargin + j * 20); 152 | } 153 | onSwipeCardListener.onSwiping(0, cardBalanceCount, cardCount, stackLength); 154 | } 155 | 156 | @TargetApi(Build.VERSION_CODES.HONEYCOMB) 157 | private final class CardOnTouchListener implements OnTouchListener { 158 | @Override 159 | public boolean onTouch(View v, MotionEvent event) { 160 | if (event.getAction() == MotionEvent.ACTION_DOWN) { 161 | distanceX = 0; 162 | startRawX = event.getRawX(); 163 | startRawY = event.getRawY(); 164 | } else if (event.getAction() == MotionEvent.ACTION_MOVE) { 165 | distanceX = (int) (event.getRawX() - startRawX); 166 | float angle = swipeDistance2Rotation(distanceX); 167 | v.setRotation(angle); 168 | v.layout(cardLeftMargin + distanceX, getPaddingTop() + cardTopMargin, cardWidth + distanceX + cardRightMargin, cardHeight + getPaddingTop() + cardTopMargin); 169 | int swipeStatus = 0; 170 | if (Math.abs(distanceX) <= swipeDistance) { 171 | swipeStatus = 0; 172 | } else if (distanceX < 0) { 173 | swipeStatus = -1; 174 | } else if (distanceX > 0) { 175 | swipeStatus = 1; 176 | } 177 | onSwipeCardListener.onSwiping(swipeStatus, cardBalanceCount, cardCount, stackLength); 178 | } else if (event.getAction() == MotionEvent.ACTION_UP) { 179 | if (Math.abs(distanceX) < swipeDistance) { 180 | ReLayoutThread reLayoutThread = new ReLayoutThread(); 181 | swipingCardView = v; 182 | reLayoutThread.start(); 183 | } else { 184 | ReMoveThread reMoveThread = new ReMoveThread(); 185 | swipingCardView = v; 186 | reMoveThread.start(); 187 | } 188 | } 189 | return true; 190 | } 191 | } 192 | 193 | private final class ReLayoutThread extends Thread { 194 | @Override 195 | public void run() { 196 | for (int i = distanceX; i > 0; ) { 197 | i = i - 50; 198 | distanceX = i; 199 | reLayoutHandler.sendEmptyMessage(100); 200 | SystemClock.sleep(40); 201 | } 202 | for (int i = distanceX; i < 0; ) { 203 | i = i + 50; 204 | distanceX = i; 205 | reLayoutHandler.sendEmptyMessage(100); 206 | SystemClock.sleep(40); 207 | } 208 | distanceX = 0; 209 | reLayoutHandler.sendEmptyMessage(100); 210 | } 211 | } 212 | 213 | private final class ReLayoutHandler extends Handler { 214 | @Override 215 | public void handleMessage(Message msg) { 216 | super.handleMessage(msg); 217 | float angle = swipeDistance2Rotation(distanceX); 218 | //KLog.e("ReLayoutHandler = "+cardWidth+" distanceX = "+ distanceX+" "+(cardWidth + distanceX)); 219 | swipingCardView.layout(cardLeftMargin + distanceX, getPaddingTop() + cardTopMargin, cardWidth + distanceX + cardRightMargin, cardHeight + getPaddingTop() + cardTopMargin); 220 | swipingCardView.setRotation(angle); 221 | } 222 | } 223 | 224 | private final class ReMoveThread extends Thread { 225 | @Override 226 | public void run() { 227 | super.run(); 228 | for (int i = distanceX; (distanceX < 0) && i > (-radius); ) { 229 | i = i - 100; 230 | distanceX = i; 231 | reMoveHandler.sendEmptyMessage(whatRemoving); 232 | if (i > -800) { 233 | SystemClock.sleep(40); 234 | } else { 235 | SystemClock.sleep(5); 236 | } 237 | //KLog.e("distanceX = "+distanceX+" radius = "+radius); 238 | } 239 | for (int i = distanceX; (distanceX > 0) && (i < radius); ) { 240 | i = i + 100; 241 | distanceX = i; 242 | reMoveHandler.sendEmptyMessage(whatRemoving); 243 | if (i < 800) { 244 | SystemClock.sleep(40); 245 | } else { 246 | SystemClock.sleep(5); 247 | } 248 | } 249 | reMoveHandler.sendEmptyMessage(whatRemoved); 250 | } 251 | } 252 | 253 | private final class ReMoveHandler extends Handler { 254 | @Override 255 | public void handleMessage(Message msg) { 256 | super.handleMessage(msg); 257 | if (msg.what == whatRemoving) { 258 | float angle = swipeDistance2Rotation(distanceX); 259 | swipingCardView.layout(cardLeftMargin + distanceX, getPaddingTop() + cardTopMargin, cardWidth + distanceX + cardRightMargin, cardHeight + getPaddingTop() + cardTopMargin); 260 | swipingCardView.setRotation(angle); 261 | } else if (msg.what == whatRemoved) { 262 | if (cardBalanceCount > stackLength) { 263 | swipingCardView.layout(cardLeftMargin, getPaddingTop() + cardTopMargin, cardWidth + cardRightMargin, cardHeight + getPaddingTop() + cardTopMargin); 264 | swipingCardView.setRotation(0); 265 | } else if (cardBalanceCount > 0) { 266 | removeViewAt(getChildCount() - 1); 267 | invalidate(); 268 | } 269 | cardBalanceCount = cardBalanceCount - 1; 270 | if (onSwipeCardListener != null) { 271 | onSwipeCardListener.onSwipeFinish(cardBalanceCount, cardCount, stackLength); 272 | } 273 | } 274 | } 275 | } 276 | 277 | private float swipeDistance2Rotation(float distance) { 278 | return (float) (Math.asin(distance / radius) * 180 / Math.PI); 279 | } 280 | /** 281 | * 获得屏幕宽度 282 | */ 283 | private int getScreenWidth(Context context) { 284 | WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 285 | DisplayMetrics outMetrics = new DisplayMetrics(); 286 | wm.getDefaultDisplay().getMetrics(outMetrics); 287 | return outMetrics.widthPixels; 288 | } 289 | /** 290 | * 数据转换: dp---->px 291 | */ 292 | private float dp2Px(float dp) { 293 | if (getContext() == null) { 294 | return -1; 295 | } 296 | return dp * getContext().getResources().getDisplayMetrics().density; 297 | } 298 | 299 | /** 300 | * 获得这个View的高度 301 | * 测量这个view,最后通过getMeasuredHeight()获取高度. 302 | * 303 | * @param view 要测量的view 304 | * @return 测量过的view的高度 305 | */ 306 | private int getViewHeight(View view) { 307 | measureView(view); 308 | return view.getMeasuredHeight(); 309 | } 310 | 311 | private void measureView(View view) { 312 | ViewGroup.LayoutParams p = view.getLayoutParams(); 313 | if (p == null) { 314 | p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); 315 | } 316 | int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0 + 0, p.width); 317 | int lpHeight = p.height; 318 | int childHeightSpec; 319 | if (lpHeight > 0) { 320 | childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY); 321 | } else { 322 | childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); 323 | } 324 | view.measure(childWidthSpec, childHeightSpec); 325 | } 326 | 327 | public void setOnSwipeCardListener(OnSwipeCardListener onSwipeCardListener) { 328 | this.onSwipeCardListener = onSwipeCardListener; 329 | } 330 | 331 | public interface OnSwipeCardListener { 332 | /** 333 | * @param cardBalanceCount 卡片的剩余个数 334 | * @param cardCount 卡片的总个数 335 | * @param stackLength 堆栈的原始长度 336 | */ 337 | public void onSwipeFinish(int cardBalanceCount, int cardCount, int stackLength); 338 | 339 | /** 340 | * @param cardBalanceCount 卡片的剩余个数 341 | * @param cardCount 卡片的总个数 342 | * @param stackLength 堆栈的原始长度 343 | * @param swipeStatus [-1, 偏左] [0, 居中] [1, 偏右] 344 | */ 345 | public void onSwiping(int swipeStatus, int cardBalanceCount, int cardCount, int stackLength); 346 | } 347 | } 348 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg9_normal.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alex-Cin/SwipeCardLayout/fa3859bbc739e6496381f625f3211b264fcfd26b/app/src/main/res/drawable/bg9_normal.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg9_selected.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alex-Cin/SwipeCardLayout/fa3859bbc739e6496381f625f3211b264fcfd26b/app/src/main/res/drawable/bg9_selected.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/selected_left.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/selected_right.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/selected_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 20 | 21 | 29 | 30 | 43 | 44 | 57 | 58 |