Android扑克牌抽奖View,android自定义层级view,扑克牌堆叠效果,cascadeLayout

需要自定义一个组件CascadeLayout,让子view可以像拿扑克牌那样的层叠起来,主要实现效果:

0818b9ca8b590ca3270a3433284dd417.png

为了设置子view之前的偏移距离,这里需要定义子view相对于上一张卡片的的左边距,上边距。然后每张卡片也可以单独的设置自己需要的边距。这个边距和不是指的内边距(padding)和外边距(margin),而是两张卡片之间的偏移距离。

在attrs中定义属性:

其中,Cascade属性集为组件对子view设置的统一属性,每个view默认使用两个属性定义的相对于上一张卡片的左边距和上边距;Cascade_LayoutParams属性集为子view单独为自己设置的对于上一张卡片的左边距和上边距,如果子view定义了这两个属性,则会覆盖掉CascadeLayout 定义的边距;

构造方法中读取这两个属性:

public CascadeLayout(Context context, AttributeSet attrs) {

super(context, attrs);

TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.Cascade_LayoutParams);

try {

leftCardPadding = ta.getDimensionPixelSize(R.styleable.Cascade_leftCardPadding, context.getResources().getDimensionPixelSize(R.dimen.default_leftCardPadding));

topCardPadding = ta.getDimensionPixelSize(R.styleable.Cascade_topCardPadding, context.getResources().getDimensionPixelSize(R.dimen.default_leftCardPadding));

Log.e("TAG", "leftCardPadding:" + leftCardPadding);

Log.e("TAG", "topCardPadding:" + topCardPadding);

} finally {

if (ta != null) ta.recycle();

}

}

同样的,构造CascadeLayout.LayoutParams 来读取子view单独设置的属性:layoutLeftCardPadding,layoutTopCardPadding:

public static class LayoutParams extends ViewGroup.LayoutParams {

int x, y;//相对于father的位置

int layoutLeftCardPadding;//自己需要显示的左边的尺寸

int layoutTopCardPadding;//自己需要显示的上边的尺寸

public LayoutParams(Context c, AttributeSet attrs) {

super(c, attrs);

TypedArray ta = c.obtainStyledAttributes(attrs, R.styleable.Cascade_LayoutParams);

try {

layoutLeftCardPadding = ta.getDimensionPixelSize(R.styleable.Cascade_LayoutParams_layout_leftCardPadding, -1);

layoutTopCardPadding = ta.getDimensionPixelSize(R.styleable.Cascade_LayoutParams_layout_topCardPadding, -1);

Log.e("TAG", "leftCardPadding:" + layoutLeftCardPadding);

Log.e("TAG", "topCardPadding:" + layoutTopCardPadding);

} finally {

if (ta != null) ta.recycle();

}

}

public LayoutParams(int width, int height) {

super(width, height);

}

public LayoutParams(ViewGroup.LayoutParams source) {

super(source);

}

}

LayoutParams的x,y用于保存自己在CascadeLayout中的位置。

属性设定好之后,就可以通过onMeasure方法和onLayout方法来计算尺寸和布局。

onMeasure方法除了需要计算自己的尺寸之外,还需要需要计算每一个子view的尺寸和位置。计算的时候,如果不是最后一张card,则总宽度累加一个偏移距离,最后一张不累加。然后如果有子view单独为自己设置了距离上一张卡片的边距,则覆盖CascadeLayout设置的值。

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

int width = getPaddingLeft(), height = getPaddingTop();

int count = getChildCount();

for (int i = 0; i < count; i++) {

View child = getChildAt(i);

measureChild(child, widthMeasureSpec, heightMeasureSpec);

LayoutParams params = (LayoutParams) child.getLayoutParams();

//计算下一张card的左边距和上边距

int cardPaddingLeft = params.layoutLeftCardPadding > 0 ? params.layoutLeftCardPadding : leftCardPadding;

int cardPaddingTop = params.layoutTopCardPadding > 0 ? params.layoutTopCardPadding : topCardPadding;

//保存子view的位置

params.x = width;

params.y = height;

//累加的width和height

if (i != count - 1) {

width += cardPaddingLeft;

height += cardPaddingTop;

} else {//最后一张

width += getPaddingRight() + child.getMeasuredWidth();

height += getPaddingBottom() + child.getMeasuredHeight();

}

}

/**

* resolveSize 和getDefaultSize有什么区别?

*

* getDefaultSize(size,measureSpec)优先使用measureSpec定义的大小;

* resolveSize(size,measureSpec),如果measureSpec未定义,则使用提供的大小(size),

* 否则,如果measureSpec的模式是(EXACTLY),则使用measureSpec定义的大小,

* 如果measureSpec定义的是AT_MOST,则使用两者中较小的那个

*

* 区别是当measureSpec定义的是AT_MOST(布局中为WRAP_CONTENT)的时候,getDefaultSize使用的是measureSpec定义的大小,

* resolveSize使用的是两者中较小的那个

*/

/**

* 那么这里的话,如果是WRAP_CONTENT的话,则应该使用较小的那个,使用resolveSize更为妥当

*/

setMeasuredDimension(resolveSize(width, widthMeasureSpec), resolveSize(height, heightMeasureSpec));

// setMeasuredDimension(getDefaultSize(width, widthMeasureSpec), getDefaultSize(height, heightMeasureSpec));

}

测量完之后,可以编写onLayout()了:

@Override

protected void onLayout(boolean changed, int l, int t, int r, int b) {

int count = getChildCount();

for (int i = 0; i < count; i++) {

View child = getChildAt(i);

LayoutParams params = (LayoutParams) child.getLayoutParams();

child.layout(params.x, params.y, params.x + child.getMeasuredWidth(), params.y + child.getMeasuredHeight());

}

}

最后,自定义LayoutParams需要重写以下方法,可直接从android源码中复制过来。

/**

* 自定义LayoutParams 需要重写以下方法

*/

@Override

protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {

return p instanceof CascadeLayout.LayoutParams;

}

@Override

protected ViewGroup.LayoutParams generateDefaultLayoutParams() {

return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);

}

@Override

public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {

return new LayoutParams(getContext(), attrs);

}

@Override

protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {

return new LayoutParams(p);

}

以上内容已完整记录自定义CascadeLayout的处理过程。为了美观,这里在使用的时候给子view添加了一个动画效果,其中应用到了LayoutAnimationController类,用于为子视图添加动画效果,让卡片一张接一张的显示出来。

MainActivtiy:

void testLayoutAnimation() {

cascadeLayout = (CascadeLayout) findViewById(R.id.cl1);

AnimationSet set = new AnimationSet(true);

Animation alphaAnimation = new AlphaAnimation(0f, 1f);

alphaAnimation.setDuration(300);

set.addAnimation(alphaAnimation);

Animation scaleAnimation = new ScaleAnimation(0f, 1f, 0f, 1f, 0.5f, 0.5f);

scaleAnimation.setDuration(300);

set.addAnimation(scaleAnimation);

LayoutAnimationController controller = new LayoutAnimationController(set, 0.2f);

cascadeLayout.setLayoutAnimation(controller);

}

测试使用的布局文件:

xmlns:phy="http://schemas.android.com/apk/res-auto"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical">

android:id="@+id/cl1"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:background="#cccccc"

phy:leftCardPadding="20dp"

phy:topCardPadding="30dp">

android:layout_width="100dp"

android:layout_height="200dp"

android:background="#ff0000" />

android:layout_width="100dp"

android:layout_height="200dp"

android:background="#ff0033" />

android:layout_width="100dp"

android:layout_height="200dp"

android:background="#ff0066"

phy:layout_leftCardPadding="50dp"

phy:layout_topCardPadding="80dp" />

android:layout_width="100dp"

android:layout_height="200dp"

android:background="#ff00aa" />

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值