map = new SimpleArrayMap<>();
34 | map.put(0, 2);
35 | mZoomHoverView.setSpan(map);
36 | mZoomHoverView.setAdapter(mAdapter);
37 | //设置动画监听
38 | mZoomHoverView.setOnZoomAnimatorListener(new OnZoomAnimatorListener() {
39 | @Override
40 | public void onZoomInStart(View view) {
41 | //放大动画开始
42 | view.setBackground(getResources().getDrawable(android.R.drawable.dialog_holo_light_frame));
43 | }
44 |
45 | @Override
46 | public void onZoomInEnd(View view) {
47 |
48 | }
49 |
50 | @Override
51 | public void onZoomOutStart(View view) {
52 | //缩小动画开始
53 | }
54 |
55 | @Override
56 | public void onZoomOutEnd(View view) {
57 | view.setBackgroundColor(getResources().getColor(R.color.colorAccent));
58 | }
59 | });
60 |
61 | mZoomHoverView.setOnItemSelectedListener(new OnItemSelectedListener() {
62 | @Override
63 | public void onItemSelected(View view, int position) {
64 | Toast.makeText(ZoomHoverActivity.this,"selected position="+position,Toast.LENGTH_SHORT).show();
65 | }
66 | });
67 |
68 | //设置放大动画插值器
69 | mZoomHoverView.setZoomInInterpolator(interpolator);
70 | //设置缩小动画插值器
71 | mZoomHoverView.setZoomOutInterpolator(interpolator);
72 | //同时设置两个动画的插值器
73 | mZoomHoverView.setZoomInterpolator(interpolator);
74 | //设置选中的item
75 | mZoomHoverView.setSelectedItem(position);
76 | ```
77 | ### ZoomHoverGridView
78 | **layout**
79 | ```xml
80 |
93 |
94 |
95 | ```
96 | **java**
97 | ```java
98 | mZoomHoverGridView.addSpanItem(0, 1, 2);
99 | mZoomHoverGridView.addSpanItem(3, 2, 2);
100 | mZoomHoverGridView.addSpanItem(4, 2, 1);
101 | mZoomHoverGridView.addSpanItem(5, 1, 3);
102 |
103 | mZoomHoverGridView.setAdapter(mAdapter);
104 | ```
105 | **自定义属性**
106 | ```xml
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 | ```
126 | ### Changed
127 | - 新增**ZoomHoverGridView**继承自GridLayout,可以实现拉伸行和列
128 | - 修改添加拉伸的方法,放进了view中(原来在adapter中)
129 |
130 | ### Thanks
131 | [GridBuilder](https://github.com/Eason90/GridBuilder)
132 |
133 | ### CardLayoutManager
134 |
135 | **代码完全从[CardLayoutManager](https://github.com/qibin0506/CardLayoutManager)复制,谢谢作者**
136 |
137 | 
138 |
139 | ### Horizontal Pagination RecyclerView
140 |
141 | 
142 |
143 | ### HeartView
144 | 
145 |
146 | ### License
147 | ```
148 | Copyright 2017 zyyoona7
149 |
150 | Licensed under the Apache License, Version 2.0 (the "License");
151 | you may not use this file except in compliance with the License.
152 | You may obtain a copy of the License at
153 |
154 | http://www.apache.org/licenses/LICENSE-2.0
155 |
156 | Unless required by applicable law or agreed to in writing, software
157 | distributed under the License is distributed on an "AS IS" BASIS,
158 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
159 | See the License for the specific language governing permissions and
160 | limitations under the License.
161 | ```
162 |
163 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'android-apt'
3 | android {
4 | compileSdkVersion 25
5 | buildToolsVersion '25.0.0'
6 | defaultConfig {
7 | applicationId "com.zyyoona7.customviewsets"
8 | minSdkVersion 19
9 | targetSdkVersion 25
10 | versionCode 1
11 | versionName "1.0"
12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
13 | renderscriptTargetApi 20
14 | renderscriptSupportModeEnabled true
15 | }
16 |
17 | buildTypes {
18 | release {
19 | minifyEnabled false
20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
21 | }
22 | }
23 | }
24 |
25 | dependencies {
26 | compile fileTree(dir: 'libs', include: ['*.jar'])
27 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
28 | exclude group: 'com.android.support', module: 'support-annotations'
29 | })
30 | compile 'com.android.support:design:25.2.0'
31 | compile 'com.android.support:design:25.2.0'
32 | compile 'com.android.support:appcompat-v7:25.2.0'
33 | compile 'com.jakewharton:butterknife:8.2.1'
34 | compile 'de.greenrobot:androidsvg:1.2.2-beta-1-tweaked-2'
35 | compile 'com.nineoldandroids:library:2.4.0'
36 | compile 'com.android.support:cardview-v7:25.2.0'
37 | compile 'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.1.3'
38 | compile 'com.daasuu:BubbleLayout:1.1.1'
39 | compile 'com.android.support.constraint:constraint-layout:1.0.0-alpha3'
40 | testCompile 'junit:junit:4.12'
41 | apt 'com.jakewharton:butterknife-compiler:8.2.1'
42 | }
43 |
--------------------------------------------------------------------------------
/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 D:\AndroidIDE\android-sdk-windows/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/zyyoona7/customviewsets/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.zyyoona7.customviewsets;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumentation test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.zyyoona7.customviewsets", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zyyoona7/customviewsets/BaseActivity.java:
--------------------------------------------------------------------------------
1 | package com.zyyoona7.customviewsets;
2 |
3 | import android.content.Intent;
4 | import android.os.Bundle;
5 | import android.support.annotation.Nullable;
6 | import android.support.v7.app.AppCompatActivity;
7 |
8 | import butterknife.ButterKnife;
9 | import butterknife.Unbinder;
10 |
11 | /**
12 | * Created by zyyoona7 on 2016/8/9.
13 | */
14 |
15 | public abstract class BaseActivity extends AppCompatActivity {
16 |
17 | private Unbinder mUnbinder;
18 |
19 | @Override
20 | protected void onCreate(@Nullable Bundle savedInstanceState) {
21 | super.onCreate(savedInstanceState);
22 | if (getContentViewID() != 0) {
23 | setContentView(getContentViewID());
24 | }
25 | mUnbinder= ButterKnife.bind(this);
26 | }
27 |
28 | @Override
29 | protected void onDestroy() {
30 | super.onDestroy();
31 | if(mUnbinder!=null){
32 | mUnbinder.unbind();
33 | }
34 | }
35 |
36 | protected void goTo(Class clazz){
37 | startActivity(new Intent(this,clazz));
38 | }
39 |
40 | protected abstract int getContentViewID();
41 | }
42 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zyyoona7/customviewsets/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.zyyoona7.customviewsets;
2 |
3 | import android.os.Bundle;
4 | import android.widget.Button;
5 |
6 | import com.zyyoona7.customviewsets.basic_operation.BasicOperationActivity;
7 | import com.zyyoona7.customviewsets.custom_layout_manager.CardActivity;
8 | import com.zyyoona7.customviewsets.heart.HeartActivity;
9 | import com.zyyoona7.customviewsets.pagination_rv.LayoutManagerActivity;
10 | import com.zyyoona7.customviewsets.zoom_hover.ZoomHoverActivity;
11 |
12 | import butterknife.BindView;
13 | import butterknife.OnClick;
14 |
15 | public class MainActivity extends BaseActivity {
16 | @BindView(R.id.btn_basic_operation)
17 | Button mBtnBasicOp;
18 |
19 | @BindView(R.id.btn_overlay_card)
20 | Button mBtnOverlayCard;
21 |
22 | @BindView(R.id.btn_card_layout_manager)
23 | Button mBtnCardLayoutManager;
24 | @BindView(R.id.btn_paging)
25 | Button mBtnPaging;
26 | @BindView(R.id.btn_heart)
27 | Button mBtnHeart;
28 |
29 | @Override
30 | protected void onCreate(Bundle savedInstanceState) {
31 | super.onCreate(savedInstanceState);
32 | }
33 |
34 | @Override
35 | protected int getContentViewID() {
36 | return R.layout.activity_main;
37 | }
38 |
39 | @OnClick(R.id.btn_basic_operation)
40 | void basicOpClick() {
41 | goTo(BasicOperationActivity.class);
42 | }
43 |
44 | @OnClick(R.id.btn_overlay_card)
45 | void overlayCardClick() {
46 | goTo(ZoomHoverActivity.class);
47 | }
48 |
49 | @OnClick(R.id.btn_card_layout_manager)
50 | void cardLayoutManagerBtnClick() {
51 | goTo(CardActivity.class);
52 | }
53 |
54 | @OnClick(R.id.btn_paging)
55 | void pagingBtnClick() {
56 | goTo(LayoutManagerActivity.class);
57 | }
58 |
59 | @OnClick(R.id.btn_heart)
60 | void heartBtnClick() {
61 | goTo(HeartActivity.class);
62 | }
63 |
64 | }
65 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zyyoona7/customviewsets/basic_operation/BasicOperationActivity.java:
--------------------------------------------------------------------------------
1 | package com.zyyoona7.customviewsets.basic_operation;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.app.AppCompatActivity;
5 |
6 | import com.zyyoona7.customviewsets.R;
7 |
8 | public class BasicOperationActivity extends AppCompatActivity {
9 |
10 | @Override
11 | protected void onCreate(Bundle savedInstanceState) {
12 | super.onCreate(savedInstanceState);
13 | setContentView(R.layout.activity_basic_operation);
14 | }
15 |
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zyyoona7/customviewsets/basic_operation/CustomBasicOpView.java:
--------------------------------------------------------------------------------
1 | package com.zyyoona7.customviewsets.basic_operation;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Color;
6 | import android.graphics.Paint;
7 | import android.graphics.Point;
8 | import android.graphics.Rect;
9 | import android.graphics.RectF;
10 | import android.util.AttributeSet;
11 | import android.view.View;
12 |
13 | import com.zyyoona7.customviewsets.utils.DensityUtils;
14 |
15 | /**
16 | * Created by zyyoona7 on 2016/8/10.
17 | *
18 | * view基本操作图形集合
19 | */
20 |
21 | public class CustomBasicOpView extends View {
22 |
23 | private static final int PAINT_COLOR = 0xff0000ff;
24 | private static final int TEXT_SIZE = 14;
25 | private static final int STOKE_WIDTH = 5;
26 | //长方形的宽高
27 | private int rectWidth = 0;
28 | private int rectHeight = 0;
29 | //间距
30 | private int margin = 0;
31 |
32 | //同坐标系的padding
33 | private int padding = 0;
34 |
35 | //画笔
36 | private Paint mPaint;
37 |
38 | //宽高
39 | private int mWidth = 0;
40 | private int mHeight = 0;
41 | /**
42 | * 利用这三个点来划分画图的范围(防止和坐标轴的图象重叠)
43 | */
44 | //起始xy点
45 | private Point startXYPoint = null;
46 | //结束的x轴点
47 | private Point endXPoint = null;
48 | //结束的y轴点
49 | private Point endYPoint = null;
50 |
51 | private int mTextHeight = 0;
52 |
53 | public CustomBasicOpView(Context context) {
54 | super(context);
55 | initPaint();
56 | }
57 |
58 | public CustomBasicOpView(Context context, AttributeSet attrs) {
59 | super(context, attrs);
60 | initPaint();
61 | mTextHeight = DensityUtils.sp2px(context, TEXT_SIZE);
62 | rectWidth = DensityUtils.dip2px(context, 150);
63 | rectHeight = DensityUtils.dip2px(context, 75);
64 | margin = DensityUtils.dip2px(context, 8);
65 | padding = DensityUtils.dip2px(context, 5);
66 | }
67 |
68 | private void initPaint() {
69 | mPaint = new Paint();
70 | mPaint.setAntiAlias(true);
71 | mPaint.setStyle(Paint.Style.STROKE);
72 | mPaint.setStrokeWidth(STOKE_WIDTH);
73 | mPaint.setStrokeCap(Paint.Cap.ROUND);
74 | mPaint.setColor(PAINT_COLOR);
75 |
76 | }
77 |
78 | /**
79 | * 改变成画字体
80 | */
81 | private void changeToTextPaint() {
82 | mPaint.setStyle(Paint.Style.FILL);
83 | mPaint.setTextSize(mTextHeight);
84 | mPaint.setColor(Color.BLACK);
85 | }
86 |
87 | private void changeStyleFill() {
88 | mPaint.setStyle(Paint.Style.FILL);
89 | }
90 |
91 | private void changeStyleStroke() {
92 | mPaint.setStyle(Paint.Style.STROKE);
93 | }
94 |
95 | private void changeColorRed() {
96 | mPaint.setColor(Color.RED);
97 | }
98 |
99 | private void changeColorBlue() {
100 | mPaint.setColor(Color.BLUE);
101 | }
102 |
103 | private void changeColorGray() {
104 | mPaint.setColor(Color.GRAY);
105 | }
106 |
107 | @Override
108 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
109 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
110 | }
111 |
112 | @Override
113 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
114 | super.onSizeChanged(w, h, oldw, oldh);
115 | mWidth = w;
116 | mHeight = h;
117 | startXYPoint = new Point(mWidth/2-rectWidth/2, 3 * margin + padding + DensityUtils.sp2px(getContext(), 16));
118 | endXPoint = new Point(mWidth - (3 * margin + padding), 3 * margin + padding + DensityUtils.sp2px(getContext(), 16));
119 | endYPoint = new Point(3 * margin + padding, mHeight - (3 * margin + padding + DensityUtils.sp2px(getContext(), 16)));
120 | }
121 |
122 | @Override
123 | protected void onDraw(Canvas canvas) {
124 | //画点
125 | canvas.save();
126 | changeColorBlue();
127 | canvas.drawPoint(startXYPoint.x + margin, startXYPoint.y + margin, mPaint);
128 | canvas.drawPoint(startXYPoint.x + 3 * margin, startXYPoint.y + margin, mPaint);
129 | canvas.drawPoint(startXYPoint.x + 5 * margin, startXYPoint.y + margin, mPaint);
130 | canvas.restore();
131 |
132 | //画线
133 | canvas.save();
134 | canvas.drawLine(startXYPoint.x, startXYPoint.y + 3 * margin, startXYPoint.x + rectWidth, startXYPoint.y + 3 * margin, mPaint);
135 | canvas.drawLine(startXYPoint.x, startXYPoint.y + 4 * margin, startXYPoint.x + 2 * rectWidth / 3, startXYPoint.y + 4 * margin, mPaint);
136 | canvas.drawLine(startXYPoint.x, startXYPoint.y + 5 * margin, startXYPoint.x + rectWidth / 2, startXYPoint.y + 5 * margin, mPaint);
137 | canvas.restore();
138 |
139 | //画长方形
140 | canvas.save();
141 | //空心长方形
142 | Rect rect = new Rect(startXYPoint.x, startXYPoint.y + 6 * margin, startXYPoint.x + rectWidth + 10, startXYPoint.y + rectHeight + 6 * margin);
143 | Rect rect1 = new Rect(startXYPoint.x, startXYPoint.y + rectHeight + 7 * margin, startXYPoint.x + rectWidth + 10, startXYPoint.y + 2 * rectHeight + 7 * margin);
144 | mPaint.setStyle(Paint.Style.STROKE);
145 | canvas.drawRect(rect, mPaint);
146 | changeToTextPaint();
147 | String strHollowRect = "我是空心长方形,Paint的style为STROKE";
148 | int hollowRectTextWidth = (int) mPaint.measureText(strHollowRect);
149 | //画文字
150 | canvas.drawText(strHollowRect, (rectWidth - hollowRectTextWidth) / 2 + startXYPoint.x, (rectHeight - mTextHeight) / 2 + startXYPoint.y + 6 * margin + mTextHeight , mPaint);
151 | //实心长方形
152 | changeColorBlue();
153 | canvas.drawRect(rect1, mPaint);
154 | changeToTextPaint();
155 | String strRect = "我是实心长方形,Paint的style为FILL";
156 | int rectWidth1 = (int) mPaint.measureText(strRect);
157 | canvas.drawText(strRect, (rectWidth - rectWidth1) / 2 + startXYPoint.x + 10, (rectHeight - mTextHeight) / 2 + startXYPoint.y + 7 * margin + rectHeight + mTextHeight , mPaint);
158 | canvas.restore();
159 |
160 | //画正方形
161 | changeColorBlue();
162 | Rect rect2 = new Rect(startXYPoint.x+rectHeight/2, startXYPoint.y + 8 * margin + 2 * rectHeight, startXYPoint.x + rectHeight+rectHeight/2, startXYPoint.y + 8 * margin + 3 * rectHeight);
163 | canvas.drawRect(rect2, mPaint);
164 | changeToTextPaint();
165 | String strSquare = "我是实心正方形";
166 | int squareWidth = (int) mPaint.measureText(strSquare);
167 | canvas.drawText(strSquare, (rectHeight - squareWidth) / 2 + startXYPoint.x+rectHeight/2, (rectHeight - mTextHeight) / 2 + startXYPoint.y + 8 * margin + 2 * rectHeight + mTextHeight , mPaint);
168 |
169 | //画椭圆
170 | RectF rect3 = new RectF(startXYPoint.x, startXYPoint.y + 9 * margin + 3 * rectHeight, startXYPoint.x + rectWidth, startXYPoint.y + 9 * margin + 4 * rectHeight);
171 | changeColorGray();
172 | canvas.drawRect(rect3, mPaint);
173 | changeColorBlue();
174 | canvas.drawOval(rect3, mPaint);
175 | changeToTextPaint();
176 | String strOval = "我是椭圆,根据我的外围长方形画出";
177 | int ovalWidth = (int) mPaint.measureText(strOval);
178 | canvas.drawText(strOval, (rectWidth - ovalWidth) / 2 + startXYPoint.x, (rectHeight - mTextHeight) / 2 + startXYPoint.y + 9 * margin + 3 * rectHeight + mTextHeight , mPaint);
179 |
180 | //画圆
181 | Rect rect4 = new Rect(startXYPoint.x+rectHeight/2, startXYPoint.y + 10 * margin + 4 * rectHeight, startXYPoint.x+rectHeight/2 + rectHeight, startXYPoint.y + 10 * margin + 5 * rectHeight);
182 | changeColorGray();
183 | canvas.drawRect(rect4, mPaint);
184 | changeColorBlue();
185 | canvas.drawCircle(startXYPoint.x+rectHeight/2 + rectHeight / 2, startXYPoint.y + 10 * margin + 4 * rectHeight + rectHeight / 2, rectHeight / 2, mPaint);
186 | changeToTextPaint();
187 | String strCircle = "我是圆";
188 | int circleWidth = (int) mPaint.measureText(strCircle);
189 | canvas.drawText(strCircle, (rectHeight - circleWidth) / 2 + startXYPoint.x+rectHeight/2, (rectHeight - mTextHeight) / 2 + startXYPoint.y + 10 * margin + 4 * rectHeight + mTextHeight , mPaint);
190 |
191 | //画椭圆圆弧
192 | //useCenter=false
193 | RectF rect5 = new RectF(startXYPoint.x , startXYPoint.y + 11 * margin + 5 * rectHeight, startXYPoint.x + rectWidth, startXYPoint.y + 11 * margin + 6 * rectHeight);
194 | changeColorGray();
195 | canvas.drawRect(rect5, mPaint);
196 | changeColorBlue();
197 | canvas.drawArc(rect5, 0, 90, false, mPaint);
198 | changeToTextPaint();
199 | String strOvalArc = "我(蓝色区域)是椭圆圆弧,useCenter=false";
200 | int ovalArcWidth = (int) mPaint.measureText(strOvalArc);
201 | canvas.drawText(strOvalArc, (rectWidth - ovalArcWidth) / 2 + startXYPoint.x, (rectHeight - mTextHeight) / 2 + startXYPoint.y + 11 * margin + 5 * rectHeight + mTextHeight , mPaint);
202 |
203 | //useCenter=true
204 | RectF rect6 = new RectF(startXYPoint.x , startXYPoint.y + 12 * margin + 6 * rectHeight, startXYPoint.x + rectWidth, startXYPoint.y + 12 * margin + 7 * rectHeight);
205 | mPaint.setColor(Color.GRAY);
206 | canvas.drawRect(rect6, mPaint);
207 | mPaint.setColor(Color.BLUE);
208 | canvas.drawArc(rect6, 0, 90, true, mPaint);
209 | changeToTextPaint();
210 | String strOvalArc1 = "我(蓝色区域)是椭圆圆弧,useCenter=true";
211 | int ovalArcWidth1 = (int) mPaint.measureText(strOvalArc1);
212 | canvas.drawText(strOvalArc1, (rectWidth - ovalArcWidth1) / 2 + startXYPoint.x , (rectHeight - mTextHeight) / 2 + startXYPoint.y + 12 * margin + 6 * rectHeight + mTextHeight , mPaint);
213 |
214 | //画圆的圆弧
215 | //useCenter=true
216 | RectF rect7 = new RectF(startXYPoint.x+rectHeight/2, startXYPoint.y + 13 * margin + 7 * rectHeight, startXYPoint.x +rectHeight/2+ rectHeight, startXYPoint.y + 13 * margin + 8 * rectHeight);
217 | changeColorGray();
218 | canvas.drawRect(rect7, mPaint);
219 | changeColorBlue();
220 | canvas.drawArc(rect7, 0, 90, true, mPaint);
221 | changeToTextPaint();
222 | String strCircleArc1 = "我(蓝色区域)是圆的圆弧,useCenter=true";
223 | int circleArcWidth1 = (int) mPaint.measureText(strCircleArc1);
224 | canvas.drawText(strCircleArc1, (rectHeight - circleArcWidth1) / 2 + startXYPoint.x+rectHeight/2, (rectHeight - mTextHeight) / 2 + startXYPoint.y + 13 * margin + 7 * rectHeight + mTextHeight , mPaint);
225 |
226 | //useCenter=false
227 | RectF rect8 = new RectF(startXYPoint.x+rectHeight/2, startXYPoint.y + 14 * margin + 8 * rectHeight, startXYPoint.x+rectHeight/2 + rectHeight, startXYPoint.y + 14 * margin + 9 * rectHeight);
228 | changeColorGray();
229 | canvas.drawRect(rect8, mPaint);
230 | changeColorBlue();
231 | canvas.drawArc(rect8, 0, 90, false, mPaint);
232 | changeToTextPaint();
233 | String strCircleArc = "我(蓝色区域)是圆的圆弧,useCenter=false";
234 | int circleArcWidth = (int) mPaint.measureText(strCircleArc);
235 | canvas.drawText(strCircleArc, (rectHeight - circleArcWidth) / 2 + startXYPoint.x+rectHeight/2, (rectHeight - mTextHeight) / 2 + startXYPoint.y + 14 * margin + 8 * rectHeight + mTextHeight , mPaint);
236 |
237 |
238 | //画饼状图
239 | RectF rect9 = new RectF(startXYPoint.x+rectHeight/2, startXYPoint.y + 15 * margin + 9 * rectHeight, startXYPoint.x +rectHeight/2+ rectHeight, startXYPoint.y + 15 * margin + 10 * rectHeight);
240 | changeColorGray();
241 | canvas.drawArc(rect9,0,60,true,mPaint);
242 | changeColorBlue();
243 | canvas.drawArc(rect9,60,90,true,mPaint);
244 | changeColorRed();
245 | canvas.drawArc(rect9,90,150,true,mPaint);
246 | mPaint.setColor(Color.BLACK);
247 | canvas.drawArc(rect9,240,120,true,mPaint);
248 |
249 | //画饼状图
250 | RectF rect10 = new RectF(startXYPoint.x+rectHeight/2, startXYPoint.y + 16 * margin + 10 * rectHeight, startXYPoint.x +rectHeight/2+ rectHeight, startXYPoint.y + 16 * margin + 11 * rectHeight);
251 | changeColorGray();
252 | canvas.drawArc(rect10,0,90,false,mPaint);
253 | changeColorBlue();
254 | canvas.drawArc(rect10,90,90,false,mPaint);
255 | changeColorRed();
256 | canvas.drawArc(rect10,180,90,false,mPaint);
257 | mPaint.setColor(Color.BLACK);
258 | canvas.drawArc(rect10,270,90,false,mPaint);
259 | }
260 | }
261 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zyyoona7/customviewsets/basic_operation/CustomCoordinateSystem.java:
--------------------------------------------------------------------------------
1 | package com.zyyoona7.customviewsets.basic_operation;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Paint;
6 | import android.util.AttributeSet;
7 | import android.view.View;
8 |
9 | import com.zyyoona7.customviewsets.utils.DensityUtils;
10 |
11 | /**
12 | * Created by zyyoona7 on 2016/8/9.
13 | *
14 | * 自定义坐标系
15 | */
16 |
17 | public class CustomCoordinateSystem extends View {
18 | private static final int LINE_WITH=5;
19 | private static final int LINE_COLOR=0xffff0000;
20 | private static final int TEXT_SIZE=16;
21 | //坐标系类型
22 | //数学坐标系
23 | private static final int TYPE_MATH=0;
24 | //手机坐标系
25 | private static final int TYPE_PHONE=1;
26 |
27 | //宽
28 | private int mWith=0;
29 | //高
30 | private int mHeight=0;
31 |
32 | //画笔对象
33 | private Paint mPaint;
34 |
35 | //和线的间距
36 | private int mMarginLine=15;
37 |
38 | //和定点的间距
39 | private int mMarginPoint=30;
40 |
41 | private int padding=0;
42 |
43 | private int mType=1;
44 |
45 | public CustomCoordinateSystem(Context context) {
46 | super(context);
47 | initPaint();
48 | }
49 |
50 | public CustomCoordinateSystem(Context context, AttributeSet attrs) {
51 | super(context, attrs);
52 | initPaint();
53 | mMarginLine=DensityUtils.dip2px(context,8);
54 | mMarginPoint=DensityUtils.dip2px(context,16);
55 | padding=DensityUtils.dip2px(context,5);
56 | }
57 |
58 | private void initPaint(){
59 | mPaint=new Paint();
60 | //设置是否使用抗锯齿功能
61 | mPaint.setAntiAlias(true);
62 | mPaint.setStyle(Paint.Style.STROKE);
63 | mPaint.setStrokeWidth(LINE_WITH);
64 | mPaint.setColor(LINE_COLOR);
65 | //当画笔样式为STROKE或FILL_OR_STROKE时,设置笔刷的图形样式,如圆形样式Cap.ROUND,或方形样式Cap.SQUARE
66 | mPaint.setStrokeCap(Paint.Cap.ROUND);
67 |
68 |
69 | }
70 |
71 | @Override
72 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
73 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
74 | }
75 |
76 | @Override
77 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
78 | super.onSizeChanged(w, h, oldw, oldh);
79 | mWith=w;
80 | mHeight=h;
81 | }
82 |
83 | @Override
84 | protected void onDraw(Canvas canvas) {
85 | canvas.save();
86 | if(mType==TYPE_MATH) {
87 | //画x轴的线
88 | canvas.drawLine(0, mHeight / 2, mWith - LINE_WITH, mHeight / 2, mPaint);
89 | //画x轴的上箭头线
90 | canvas.drawLine(mWith - mMarginPoint, mHeight / 2 - mMarginLine, mWith - LINE_WITH, mHeight / 2, mPaint);
91 | //画x轴的下箭头线
92 | canvas.drawLine(mWith - mMarginPoint, mHeight / 2 + mMarginLine, mWith - LINE_WITH, mHeight / 2, mPaint);
93 | //画y轴的线
94 | canvas.drawLine(mWith / 2, 0, mWith / 2, mHeight - LINE_WITH, mPaint);
95 | //画y轴的左箭头线
96 | canvas.drawLine(mWith / 2 - mMarginLine, mHeight - mMarginPoint, mWith / 2, mHeight - LINE_WITH, mPaint);
97 | //画y轴的右箭头线
98 | canvas.drawLine(mWith / 2 + mMarginLine, mHeight - mMarginPoint, mWith / 2, mHeight - LINE_WITH, mPaint);
99 |
100 | mPaint.setStyle(Paint.Style.FILL);
101 | mPaint.setTextSize(DensityUtils.sp2px(getContext(),TEXT_SIZE));
102 | mPaint.setColor(LINE_COLOR);
103 | //画坐标
104 | //原点坐标
105 | String oCoordinate = "(0,0)";
106 | //x轴最大坐标
107 | String xCoordinate = "(" + mWith/2 + ",0)";
108 | //y轴最大坐标
109 | String yCoordinate = "(0," + mHeight/2 + ")";
110 | //计算原点坐标文字的宽度
111 | float oPointWidth = mPaint.measureText(oCoordinate);
112 | //画原点坐标
113 | canvas.drawText(oCoordinate, mWith / 2 - padding - oPointWidth, mHeight / 2 + DensityUtils.sp2px(getContext(), TEXT_SIZE)+10, mPaint);
114 | //计算x轴坐标的宽度
115 | float xPointWidth = mPaint.measureText(xCoordinate);
116 | //画x轴坐标
117 | canvas.drawText(xCoordinate, mWith - padding - xPointWidth, mHeight / 2 + DensityUtils.sp2px(getContext(), TEXT_SIZE) + mMarginLine, mPaint);
118 | //画y轴坐标
119 | canvas.drawText(yCoordinate, mWith / 2 + mMarginLine, mHeight - padding, mPaint);
120 | }else {
121 | //画x轴的线
122 | canvas.drawLine(padding+mMarginLine,padding+mMarginLine,mWith-LINE_WITH,padding+mMarginLine,mPaint);
123 | //画x轴的上箭头线
124 | canvas.drawLine(mWith-mMarginPoint,padding,mWith-LINE_WITH,padding+mMarginLine,mPaint);
125 | //画x轴的下箭头线
126 | canvas.drawLine(mWith-mMarginPoint,padding+2*mMarginLine,mWith-LINE_WITH,padding+mMarginLine,mPaint);
127 | //画y轴的线
128 | canvas.drawLine(padding+mMarginLine,padding+mMarginLine,padding+mMarginLine,mHeight-LINE_WITH,mPaint);
129 | //画y轴的左箭头线
130 | canvas.drawLine(padding,mHeight-mMarginPoint,padding+mMarginLine,mHeight-LINE_WITH,mPaint);
131 | //画y轴的右箭头线
132 | canvas.drawLine(padding+2*mMarginLine,mHeight-mMarginPoint,padding+mMarginLine,mHeight-LINE_WITH,mPaint);
133 |
134 | mPaint.setStyle(Paint.Style.FILL);
135 | mPaint.setTextSize(DensityUtils.sp2px(getContext(),TEXT_SIZE));
136 | mPaint.setColor(LINE_COLOR);
137 | //画坐标
138 | //原点坐标
139 | String oCoordinate="(0,0)";
140 | //x轴最大坐标
141 | String xCoordinate="("+mWith+",0)";
142 | //y轴最大坐标
143 | String yCoordinate="(0,"+mHeight+")";
144 | //画原点坐标
145 | //drawText的y轴坐标的基线为底部,所以要减去字体的高度
146 | canvas.drawText(oCoordinate,2*padding+mMarginLine,2*padding+mMarginLine+DensityUtils.sp2px(getContext(),TEXT_SIZE),mPaint);
147 | //计算x轴坐标的宽度
148 | float xPointWidth=mPaint.measureText(xCoordinate);
149 | //画x轴坐标
150 | canvas.drawText(xCoordinate,mWith-padding-xPointWidth,2*padding+2*mMarginLine+DensityUtils.sp2px(getContext(),TEXT_SIZE),mPaint);
151 | //画y轴坐标
152 | canvas.drawText(yCoordinate,2*padding+2*mMarginLine,mHeight-padding,mPaint);
153 |
154 | }
155 | canvas.restore();
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zyyoona7/customviewsets/cover_flow/Collector.java:
--------------------------------------------------------------------------------
1 | package com.zyyoona7.customviewsets.cover_flow;
2 |
3 | import android.view.View;
4 |
5 | import java.util.ArrayList;
6 |
7 | public class Collector
8 | {
9 | ArrayList mOldViews = new ArrayList();
10 |
11 | protected void collect(View v)
12 | {
13 | this.mOldViews.add(v);
14 | }
15 |
16 | protected View retrieve()
17 | {
18 | if (this.mOldViews.size() == 0) {
19 | return null;
20 | }
21 | return (View)this.mOldViews.remove(0);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zyyoona7/customviewsets/cover_flow/CoverFlowActivity.java:
--------------------------------------------------------------------------------
1 | package com.zyyoona7.customviewsets.cover_flow;
2 |
3 | import android.os.Bundle;
4 | import android.view.View;
5 | import android.view.ViewGroup;
6 | import android.widget.BaseAdapter;
7 | import android.widget.ImageView;
8 |
9 | import com.zyyoona7.customviewsets.BaseActivity;
10 | import com.zyyoona7.customviewsets.R;
11 |
12 | import butterknife.BindView;
13 |
14 | public class CoverFlowActivity extends BaseActivity {
15 |
16 | @BindView(R.id.horizontal_carousel)
17 | HorizontalCarouselLayout layout;
18 |
19 | @Override
20 | protected int getContentViewID() {
21 | return R.layout.activity_cover_flow;
22 | }
23 |
24 | @Override
25 | protected void onCreate(Bundle savedInstanceState) {
26 | super.onCreate(savedInstanceState);
27 | layout.setAdapter(new ImageAdapter());
28 | layout.setStyle(new HorizontalCarouselStyle(this,HorizontalCarouselStyle.NO_STYLE));
29 | }
30 |
31 | public class ImageAdapter extends BaseAdapter{
32 |
33 | @Override
34 | public int getCount() {
35 | return 4;
36 | }
37 |
38 | @Override
39 | public Object getItem(int position) {
40 | return null;
41 | }
42 |
43 | @Override
44 | public long getItemId(int position) {
45 | return 0;
46 | }
47 |
48 | @Override
49 | public View getView(int position, View convertView, ViewGroup parent) {
50 |
51 | ImageView imageView=new ImageView(CoverFlowActivity.this);
52 | imageView.setImageResource(R.mipmap.yoona);
53 |
54 | return imageView;
55 | }
56 | }
57 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zyyoona7/customviewsets/cover_flow/HorizontalCarouselLayout.java:
--------------------------------------------------------------------------------
1 | package com.zyyoona7.customviewsets.cover_flow;
2 |
3 | import android.content.Context;
4 | import android.graphics.Camera;
5 | import android.graphics.Matrix;
6 | import android.os.SystemClock;
7 | import android.util.AttributeSet;
8 | import android.util.Log;
9 | import android.view.GestureDetector;
10 | import android.view.MotionEvent;
11 | import android.view.View;
12 | import android.view.ViewGroup;
13 | import android.view.animation.Transformation;
14 | import android.widget.BaseAdapter;
15 |
16 | public class HorizontalCarouselLayout
17 | extends ViewGroup {
18 | private final float SCALE_RATIO = 0.9F;
19 | private int mGestureSensitivity = 80;
20 | private int DURATION = 200;
21 | private int mSpaceBetweenViews = 20;
22 | private int mRotation;
23 | private boolean mRotationEnabled = false;
24 | private int mTranslate;
25 | private boolean mTranslatateEnbabled = false;
26 | private float mSetInactiveViewTransparency = 1.0F;
27 | private int mHowManyViews = 99;
28 | private float mChildSizeRatio = 0.6F;
29 | private BaseAdapter mAdapter = null;
30 | private int mCurrentItem = 0;
31 | private int mCenterView = 0;
32 | private int mChildrenWidth;
33 | private int mChildrenWidthMiddle;
34 | private int mChildrenHeight;
35 | private int mChildrenHeightMiddle;
36 | private int mHeightCenter;
37 | private int mWidthCenter;
38 | private int mMaxChildUnderCenter;
39 | private float mViewZoomOutFactor = 0.0F;
40 | private int mCoverflowRotation = 0;
41 | private Collector mCollector = new Collector();
42 | private Matrix mMatrix = new Matrix();
43 | private Context mContext;
44 | private float mGap;
45 | private boolean mIsAnimating = false;
46 | private long mCurTime;
47 | private long mStartTime;
48 | private int mItemtoReach = 0;
49 | private CarouselInterface mCallback;
50 | private Runnable animationTask = new Runnable() {
51 | public void run() {
52 | HorizontalCarouselLayout.this.mCurTime = SystemClock.uptimeMillis();
53 | long totalTime = HorizontalCarouselLayout.this.mCurTime - HorizontalCarouselLayout.this.mStartTime;
54 | if (totalTime > HorizontalCarouselLayout.this.DURATION) {
55 | if (HorizontalCarouselLayout.this.mItemtoReach > HorizontalCarouselLayout.this.mCurrentItem) {
56 | HorizontalCarouselLayout.this.fillBottom();
57 | } else {
58 | HorizontalCarouselLayout.this.fillTop();
59 | }
60 | HorizontalCarouselLayout.this.mCurrentItem = HorizontalCarouselLayout.this.mItemtoReach;
61 | HorizontalCarouselLayout.this.mGap = 0.0F;
62 | HorizontalCarouselLayout.this.mIsAnimating = false;
63 |
64 | HorizontalCarouselLayout.this.mCenterView = HorizontalCarouselLayout.this.mCurrentItem;
65 | if (HorizontalCarouselLayout.this.mCurrentItem >= HorizontalCarouselLayout.this.mMaxChildUnderCenter) {
66 | HorizontalCarouselLayout.this.mCenterView = HorizontalCarouselLayout.this.mMaxChildUnderCenter;
67 | }
68 | HorizontalCarouselLayout.this.removeCallbacks(HorizontalCarouselLayout.this.animationTask);
69 | if (HorizontalCarouselLayout.this.mCallback != null) {
70 | HorizontalCarouselLayout.this.mCallback.onItemChangedListener(mAdapter.getView(mCurrentItem, null, HorizontalCarouselLayout.this), mCurrentItem);
71 | }
72 | } else {
73 | float perCent = (float) totalTime / HorizontalCarouselLayout.this.DURATION;
74 | HorizontalCarouselLayout.this.mGap = ((HorizontalCarouselLayout.this.mCurrentItem - HorizontalCarouselLayout.this.mItemtoReach) * perCent);
75 | HorizontalCarouselLayout.this.post(this);
76 | }
77 | HorizontalCarouselLayout.this.childrenLayout(HorizontalCarouselLayout.this.mGap);
78 | HorizontalCarouselLayout.this.invalidate();
79 | }
80 | };
81 | private GestureDetector mGestureDetector = new GestureDetector(this.mContext,
82 | new GestureDetector.SimpleOnGestureListener() {
83 | public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
84 | if ((!HorizontalCarouselLayout.this.mIsAnimating) && (HorizontalCarouselLayout.this.mAdapter != null)) {
85 | int dx = (int) (e2.getX() - e1.getX());
86 | if ((Math.abs(dx) > HorizontalCarouselLayout.this.mGestureSensitivity) &&
87 | (Math.abs(velocityY) < Math.abs(velocityX))) {
88 | if (velocityX > 0.0F) {
89 | if (HorizontalCarouselLayout.this.mCurrentItem > 0) {
90 | HorizontalCarouselLayout.this.mItemtoReach = (HorizontalCarouselLayout.this.mCurrentItem - 1);
91 | HorizontalCarouselLayout.this.mStartTime = SystemClock.uptimeMillis();
92 | HorizontalCarouselLayout.this.mIsAnimating = true;
93 | HorizontalCarouselLayout.this.post(HorizontalCarouselLayout.this.animationTask);
94 | return true;
95 | }
96 | } else if (HorizontalCarouselLayout.this.mCurrentItem < HorizontalCarouselLayout.this.mAdapter.getCount() - 1) {
97 | HorizontalCarouselLayout.this.mItemtoReach = (HorizontalCarouselLayout.this.mCurrentItem + 1);
98 | HorizontalCarouselLayout.this.mStartTime = SystemClock.uptimeMillis();
99 | HorizontalCarouselLayout.this.mIsAnimating = true;
100 | HorizontalCarouselLayout.this.post(HorizontalCarouselLayout.this.animationTask);
101 | return true;
102 | }
103 | }
104 | }
105 | return false;
106 | }
107 | }
108 | );
109 |
110 | public HorizontalCarouselLayout(Context context) {
111 | super(context);
112 | this.mContext = context;
113 | initSlidingAnimation();
114 | }
115 |
116 | public HorizontalCarouselLayout(Context context, AttributeSet attrs) {
117 | super(context, attrs);
118 | this.mContext = context;
119 | initSlidingAnimation();
120 | }
121 |
122 | public HorizontalCarouselLayout(Context context, AttributeSet attrs, int defStyle) {
123 | super(context, attrs, defStyle);
124 | this.mContext = context;
125 | initSlidingAnimation();
126 | }
127 |
128 | public void disableRotation() {
129 | this.mRotationEnabled = false;
130 | }
131 |
132 | public void disableTranslate() {
133 | this.mTranslatateEnbabled = false;
134 | }
135 |
136 | public void setOnCarouselViewChangedListener(CarouselInterface carouselInterface) {
137 | this.mCallback = carouselInterface;
138 | }
139 |
140 | public void setGestureSensitivity(int gestureSensitivity) {
141 | this.mGestureSensitivity = gestureSensitivity;
142 | }
143 |
144 | public void setStyle(HorizontalCarouselStyle style) {
145 | this.mSetInactiveViewTransparency = style.getInactiveViewTransparency();
146 | this.mSpaceBetweenViews = style.getSpaceBetweenViews();
147 | this.mRotation = style.getRotation();
148 | this.mRotationEnabled = style.isRotationEnabled();
149 | this.mTranslate = style.getTranslate();
150 | this.mTranslatateEnbabled = style.isTranslatateEnbabled();
151 | this.mHowManyViews = style.getHowManyViews();
152 | this.mChildSizeRatio = style.getChildSizeRatio();
153 | this.mCoverflowRotation = style.getCoverflowRotation();
154 | this.mViewZoomOutFactor = style.getViewZoomOutFactor();
155 | this.DURATION = style.getAnimationTime();
156 | }
157 |
158 | public void setAdapter(BaseAdapter adapter) {
159 | if (adapter != null) {
160 | this.mAdapter = adapter;
161 | this.mCenterView = (this.mCurrentItem = 0);
162 | if (this.mHowManyViews % 2 == 0) {
163 | this.mMaxChildUnderCenter = (this.mHowManyViews / 2);
164 | } else {
165 | this.mMaxChildUnderCenter = (this.mHowManyViews / 2);
166 | }
167 | for (int i = 0; i <= this.mMaxChildUnderCenter; i++) {
168 | if (i > this.mAdapter.getCount() - 1) {
169 | break;
170 | }
171 | View v = this.mAdapter.getView(i, null, this);
172 | addView(v);
173 | }
174 | childrenLayout(0.0F);
175 | invalidate();
176 | }
177 | }
178 |
179 | private void fillTop() {
180 | if ((this.mCenterView < this.mMaxChildUnderCenter) &&
181 | (getChildCount() > this.mMaxChildUnderCenter + 1)) {
182 | View old = getChildAt(getChildCount() - 1);
183 | detachViewFromParent(old);
184 | this.mCollector.collect(old);
185 | }
186 | if (getChildCount() >= this.mHowManyViews) {
187 | View old = getChildAt(this.mHowManyViews - 1);
188 | detachViewFromParent(old);
189 | this.mCollector.collect(old);
190 | }
191 | int indexToRequest = this.mCurrentItem - (this.mMaxChildUnderCenter + 1);
192 | if (indexToRequest >= 0) {
193 | Log.v("UITEST", "Fill top with " + indexToRequest);
194 | View recycled = this.mCollector.retrieve();
195 | View v = this.mAdapter.getView(indexToRequest, recycled, this);
196 | if (recycled != null) {
197 | attachViewToParent(v, 0, generateDefaultLayoutParams());
198 | v.measure(this.mChildrenWidth, this.mChildrenHeight);
199 | } else {
200 | addView(v, 0);
201 | }
202 | }
203 | }
204 |
205 | private void fillBottom() {
206 | if (this.mCenterView >= this.mMaxChildUnderCenter) {
207 | View old = getChildAt(0);
208 | detachViewFromParent(old);
209 | this.mCollector.collect(old);
210 | }
211 | if (getChildCount() >= this.mHowManyViews) {
212 | View old = getChildAt(0);
213 | detachViewFromParent(old);
214 | this.mCollector.collect(old);
215 | }
216 | int indexToRequest = this.mCurrentItem + (this.mMaxChildUnderCenter + 1);
217 | if (indexToRequest < this.mAdapter.getCount()) {
218 | Log.v("UITEST", "Fill bottom with " + indexToRequest);
219 | View recycled = this.mCollector.retrieve();
220 | View v = this.mAdapter.getView(indexToRequest, recycled, this);
221 | if (recycled != null) {
222 | Log.v("UITEST", "view attached");
223 | attachViewToParent(v, -1, generateDefaultLayoutParams());
224 | v.measure(this.mChildrenWidth, this.mChildrenHeight);
225 | } else {
226 | Log.v("UITEST", "view added");
227 | addView(v, -1);
228 | }
229 | }
230 | }
231 |
232 | protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
233 | return new ViewGroup.LayoutParams(-1,
234 | -1);
235 | }
236 |
237 | protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
238 | return new ViewGroup.LayoutParams(p);
239 | }
240 |
241 | private void initSlidingAnimation() {
242 | setChildrenDrawingOrderEnabled(true);
243 | setStaticTransformationsEnabled(true);
244 | setOnTouchListener(new View.OnTouchListener() {
245 | public boolean onTouch(View v, MotionEvent event) {
246 | HorizontalCarouselLayout.this.mGestureDetector.onTouchEvent(event);
247 | return true;
248 | }
249 | });
250 | }
251 |
252 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
253 | int count = getChildCount();
254 | int specWidthSize = View.MeasureSpec.getSize(widthMeasureSpec);
255 | int specHeightSize = View.MeasureSpec.getSize(heightMeasureSpec);
256 | this.mWidthCenter = (specWidthSize / 2);
257 | this.mHeightCenter = (specHeightSize / 2);
258 | this.mChildrenWidth = ((int) (specWidthSize * this.mChildSizeRatio));
259 | this.mChildrenHeight = ((int) (specHeightSize * this.mChildSizeRatio));
260 | this.mChildrenWidthMiddle = (this.mChildrenWidth / 2);
261 | this.mChildrenHeightMiddle = (this.mChildrenHeight / 2);
262 | for (int i = 0; i < count; i++) {
263 | View child = getChildAt(i);
264 | measureChild(child, this.mChildrenWidth, this.mChildrenHeight);
265 | }
266 | setMeasuredDimension(specWidthSize, specHeightSize);
267 | }
268 |
269 | protected void onLayout(boolean changed, int l, int t, int r, int b) {
270 | childrenLayout(0.0F);
271 | }
272 |
273 | private void childrenLayout(float gap) {
274 | int leftCenterView = this.mWidthCenter - this.mChildrenWidth / 2;
275 | int topCenterView = this.mHeightCenter - this.mChildrenHeight / 2;
276 | int count = getChildCount();
277 | for (int i = 0; i < count; i++) {
278 | View child = getChildAt(i);
279 | float offset = this.mCenterView - i - gap;
280 | int left = (int) (leftCenterView - this.mSpaceBetweenViews * offset);
281 | child.layout(left, topCenterView, left + this.mChildrenWidth,
282 | topCenterView + this.mChildrenHeight);
283 | }
284 | }
285 |
286 | protected int getChildDrawingOrder(int childCount, int i) {
287 | int centerView = this.mCenterView;
288 | if (this.mGap > 0.5F) {
289 | centerView--;
290 | } else if (this.mGap < -0.5F) {
291 | centerView++;
292 | }
293 | if (i < centerView) {
294 | return i;
295 | }
296 | if (i > centerView) {
297 | return centerView + (childCount - 1) - i;
298 | }
299 | return childCount - 1;
300 | }
301 |
302 | protected boolean getChildStaticTransformation(View child, Transformation t) {
303 | Camera camera = new Camera();
304 | int leftCenterView = this.mWidthCenter - this.mChildrenWidthMiddle;
305 | float offset = (-child.getLeft() + leftCenterView) /
306 | this.mSpaceBetweenViews;
307 | if (offset != 0.0F) {
308 | float absOffset = Math.abs(offset);
309 | float scale = (float) Math.pow(0.8999999761581421D, absOffset);
310 | t.clear();
311 | t.setTransformationType(Transformation.TYPE_MATRIX);
312 | t.setAlpha(this.mSetInactiveViewTransparency);
313 | Matrix m = t.getMatrix();
314 | m.setScale(scale, scale);
315 | if (this.mTranslatateEnbabled) {
316 | m.setTranslate(0.0F, this.mTranslate * absOffset);
317 | }
318 | if (offset > 0.0F) {
319 | camera.save();
320 | camera.translate(0.0F, 0.0F, this.mViewZoomOutFactor * offset);
321 | camera.rotateY(this.mCoverflowRotation);
322 | camera.getMatrix(m);
323 | camera.restore();
324 | m.preTranslate(-this.mChildrenWidthMiddle, -this.mChildrenHeight);
325 | m.postTranslate(this.mChildrenWidthMiddle, this.mChildrenHeight);
326 | } else {
327 | camera.save();
328 | camera.translate(0.0F, 0.0F, -(this.mViewZoomOutFactor * offset));
329 | camera.rotateY(-this.mCoverflowRotation);
330 | camera.getMatrix(m);
331 | camera.restore();
332 | m.preTranslate(-this.mChildrenWidthMiddle, -this.mChildrenHeight);
333 | m.postTranslate(this.mChildrenWidthMiddle, this.mChildrenHeight);
334 | }
335 | this.mMatrix.reset();
336 | if (this.mRotationEnabled) {
337 | this.mMatrix.setRotate(this.mRotation * offset);
338 | }
339 | this.mMatrix.preTranslate(-this.mChildrenWidthMiddle, -this.mChildrenHeightMiddle);
340 | this.mMatrix.postTranslate(this.mChildrenWidthMiddle, this.mChildrenHeightMiddle);
341 | m.setConcat(m, this.mMatrix);
342 | }
343 | return true;
344 | }
345 |
346 | public static abstract interface CarouselInterface {
347 | public abstract void onItemChangedListener(View paramView, int paramInt);
348 | }
349 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zyyoona7/customviewsets/cover_flow/HorizontalCarouselStyle.java:
--------------------------------------------------------------------------------
1 | package com.zyyoona7.customviewsets.cover_flow;
2 |
3 | import android.content.Context;
4 | import android.util.TypedValue;
5 |
6 | public class HorizontalCarouselStyle
7 | {
8 | public static final int NO_STYLE = 0;
9 | public static final int STYLE_ZOOMED_OUT = 1;
10 | public static final int STYLE_COVERFLOW = 2;
11 | public static final int STYLE_RIGHT_ALIGNED_ROTATION = 3;
12 | public static final int STYLE_LEFT_ALIGNED_ROTATION = 4;
13 | private int mCoverflowRotation;
14 | private float mViewZoomOutFactor;
15 | private float mInactiveViewTransparency;
16 | private int mSpaceBetweenViews;
17 | private int mRotation;
18 | private boolean mRotationEnabled;
19 | private int mTranslate;
20 | private boolean mTranslatateEnbabled;
21 | private int mHowManyViews;
22 | private float mChildSizeRatio;
23 | private int mAnimationTime;
24 | private int dip;
25 |
26 | public HorizontalCarouselStyle(Context context, int style)
27 | {
28 | this.dip = ((int) TypedValue.applyDimension(1, 1.0F, context.getResources().getDisplayMetrics()));
29 | this.mInactiveViewTransparency = 1.0F;
30 | this.mHowManyViews = 99;
31 | this.mChildSizeRatio = 0.6F;
32 | this.mAnimationTime = 200;
33 | setStyle(style);
34 | }
35 |
36 | private void setStyle(int style)
37 | {
38 | switch (style)
39 | {
40 | case 1:
41 | this.mSpaceBetweenViews = (80 * this.dip);
42 | this.mRotation = 0;
43 | this.mRotationEnabled = false;
44 | this.mTranslate = 50;
45 | this.mCoverflowRotation = 0;
46 | this.mViewZoomOutFactor = 30.0F;
47 | this.mTranslatateEnbabled = true;
48 | break;
49 | case 2:
50 | this.mSpaceBetweenViews = (80 * this.dip);
51 | this.mRotation = 0;
52 | this.mRotationEnabled = false;
53 | this.mTranslate = -50;
54 | this.mCoverflowRotation = 45;
55 | this.mViewZoomOutFactor = 60.0F;
56 | this.mTranslatateEnbabled = true;
57 | break;
58 | case 3:
59 | this.mSpaceBetweenViews = (80 * this.dip);
60 | this.mRotation = 30;
61 | this.mRotationEnabled = true;
62 | this.mTranslate = 50;
63 | this.mCoverflowRotation = 0;
64 | this.mViewZoomOutFactor = 0.0F;
65 | this.mTranslatateEnbabled = true;
66 | break;
67 | case 4:
68 | this.mSpaceBetweenViews = (80 * this.dip);
69 | this.mRotation = -30;
70 | this.mRotationEnabled = true;
71 | this.mTranslate = -50;
72 | this.mCoverflowRotation = 0;
73 | this.mViewZoomOutFactor = 0.0F;
74 | this.mTranslatateEnbabled = true;
75 | break;
76 | default:
77 | this.mSpaceBetweenViews = (50 * this.dip);
78 | this.mRotation = 0;
79 | this.mRotationEnabled = false;
80 | this.mTranslate = 0;
81 | this.mCoverflowRotation = 0;
82 | this.mViewZoomOutFactor = 0.0F;
83 | this.mTranslatateEnbabled = false;
84 | }
85 | }
86 |
87 | public void setInactiveViewTransparency(float mSetInactiveViewTransparency)
88 | {
89 | this.mInactiveViewTransparency = mSetInactiveViewTransparency;
90 | }
91 |
92 | public void setSpaceBetweenViews(int spaceInPixel)
93 | {
94 | this.mSpaceBetweenViews = (spaceInPixel * this.dip);
95 | }
96 |
97 | public void setRotation(int rotation)
98 | {
99 | this.mRotationEnabled = true;
100 | this.mRotation = rotation;
101 | }
102 |
103 | public void setTranslate(int translate)
104 | {
105 | this.mTranslatateEnbabled = true;
106 | this.mTranslate = translate;
107 | }
108 |
109 | public boolean setHowManyViews(int howMany)
110 | {
111 | if (howMany % 2 != 0) {
112 | return false;
113 | }
114 | this.mHowManyViews = howMany;
115 | return true;
116 | }
117 |
118 | public boolean setChildSizeRation(float parentPerCent)
119 | {
120 | if ((parentPerCent > 1.0F) && (parentPerCent < 1.0F)) {
121 | return false;
122 | }
123 | this.mChildSizeRatio = parentPerCent;
124 | return true;
125 | }
126 |
127 | public void setAnimationTime(int mAnimationTime)
128 | {
129 | this.mAnimationTime = mAnimationTime;
130 | }
131 |
132 | public void setCoverflowRotation(int mCoverflowRotation)
133 | {
134 | this.mCoverflowRotation = mCoverflowRotation;
135 | }
136 |
137 | public void setViewZoomOutFactor(float mViewZoomOutFactor)
138 | {
139 | this.mViewZoomOutFactor = mViewZoomOutFactor;
140 | }
141 |
142 | public float getInactiveViewTransparency()
143 | {
144 | return this.mInactiveViewTransparency;
145 | }
146 |
147 | public int getSpaceBetweenViews()
148 | {
149 | return this.mSpaceBetweenViews;
150 | }
151 |
152 | public int getRotation()
153 | {
154 | return this.mRotation;
155 | }
156 |
157 | public boolean isRotationEnabled()
158 | {
159 | return this.mRotationEnabled;
160 | }
161 |
162 | public int getTranslate()
163 | {
164 | return this.mTranslate;
165 | }
166 |
167 | public boolean isTranslatateEnbabled()
168 | {
169 | return this.mTranslatateEnbabled;
170 | }
171 |
172 | public int getHowManyViews()
173 | {
174 | return this.mHowManyViews;
175 | }
176 |
177 | public float getChildSizeRatio()
178 | {
179 | return this.mChildSizeRatio;
180 | }
181 |
182 | public int getAnimationTime()
183 | {
184 | return this.mAnimationTime;
185 | }
186 |
187 | public int getCoverflowRotation()
188 | {
189 | return this.mCoverflowRotation;
190 | }
191 |
192 | public float getViewZoomOutFactor()
193 | {
194 | return this.mViewZoomOutFactor;
195 | }
196 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zyyoona7/customviewsets/cover_flow/ScrollerLayout.java:
--------------------------------------------------------------------------------
1 | package com.zyyoona7.customviewsets.cover_flow;
2 |
3 | import android.content.Context;
4 | import android.support.v4.view.ViewConfigurationCompat;
5 | import android.util.AttributeSet;
6 | import android.util.Log;
7 | import android.view.MotionEvent;
8 | import android.view.View;
9 | import android.view.ViewConfiguration;
10 | import android.view.ViewGroup;
11 | import android.widget.Scroller;
12 |
13 | /**
14 | * Created by User on 2016/8/22.
15 | */
16 |
17 | public class ScrollerLayout extends ViewGroup {
18 |
19 | private static final String TAG = "ScrollerLayout";
20 |
21 | /**
22 | * 用于完成滚动操作的实例
23 | */
24 | private Scroller mScroller;
25 |
26 | /**
27 | * 判定为拖动的最小移动像素数
28 | */
29 | private int mTouchSlop;
30 |
31 | /**
32 | * 手机按下时的屏幕坐标
33 | */
34 | private float mXDown;
35 |
36 | /**
37 | * 手机当时所处的屏幕坐标
38 | */
39 | private float mXMove;
40 |
41 | /**
42 | * 上次触发ACTION_MOVE事件时的屏幕坐标
43 | */
44 | private float mXLastMove;
45 |
46 | /**
47 | * 界面可滚动的左边界
48 | */
49 | private int leftBorder;
50 |
51 | /**
52 | * 界面可滚动的右边界
53 | */
54 | private int rightBorder;
55 |
56 | public ScrollerLayout(Context context, AttributeSet attrs) {
57 | super(context, attrs);
58 | // 第一步,创建Scroller的实例
59 | mScroller = new Scroller(context);
60 | ViewConfiguration configuration = ViewConfiguration.get(context);
61 | // 获取TouchSlop值
62 | mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);
63 | }
64 |
65 | @Override
66 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
67 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
68 | int childCount = getChildCount();
69 | for (int i = 0; i < childCount; i++) {
70 | View childView = getChildAt(i);
71 | // 为ScrollerLayout中的每一个子控件测量大小
72 | measureChild(childView, widthMeasureSpec, heightMeasureSpec);
73 | }
74 | }
75 |
76 | @Override
77 | protected void onLayout(boolean changed, int l, int t, int r, int b) {
78 | if (changed) {
79 | int childCount = getChildCount();
80 | for (int i = 0; i < childCount; i++) {
81 | View childView = getChildAt(i);
82 | // 为ScrollerLayout中的每一个子控件在水平方向上进行布局
83 | childView.layout(i * childView.getMeasuredWidth(), 0, (i + 1) * childView.getMeasuredWidth(), childView.getMeasuredHeight());
84 | }
85 | // 初始化左右边界值
86 | leftBorder = getChildAt(0).getLeft();
87 | rightBorder = getChildAt(getChildCount() - 1).getRight();
88 | }
89 | }
90 |
91 | @Override
92 | public boolean onInterceptTouchEvent(MotionEvent ev) {
93 | switch (ev.getAction()) {
94 | case MotionEvent.ACTION_DOWN:
95 | mXDown = ev.getRawX();
96 | mXLastMove = mXDown;
97 | break;
98 | case MotionEvent.ACTION_MOVE:
99 | mXMove = ev.getRawX();
100 | float diff = Math.abs(mXMove - mXDown);
101 | mXLastMove = mXMove;
102 | // 当手指拖动值大于TouchSlop值时,认为应该进行滚动,拦截子控件的事件
103 | if (diff > mTouchSlop) {
104 | return true;
105 | }
106 | break;
107 | }
108 | return super.onInterceptTouchEvent(ev);
109 | }
110 |
111 | @Override
112 | public boolean onTouchEvent(MotionEvent event) {
113 | switch (event.getAction()) {
114 | case MotionEvent.ACTION_MOVE:
115 | mXMove = event.getRawX();
116 | int scrolledX = (int) (mXLastMove - mXMove);
117 | if (getScrollX() + scrolledX < leftBorder) {
118 | scrollTo(leftBorder, 0);
119 | return true;
120 | } else if (getScrollX() + getWidth() + scrolledX > rightBorder) {
121 | scrollTo(rightBorder - getWidth(), 0);
122 | return true;
123 | }
124 | scrollBy(scrolledX, 0);
125 | mXLastMove = mXMove;
126 | break;
127 | case MotionEvent.ACTION_UP:
128 | // 当手指抬起时,根据当前的滚动值来判定应该滚动到哪个子控件的界面
129 | int targetIndex = (getScrollX() + getWidth() / 2) / getWidth();
130 | int dx = targetIndex * getWidth() - getScrollX();
131 | // 第二步,调用startScroll()方法来初始化滚动数据并刷新界面
132 | mScroller.startScroll(getScrollX(), 0, dx, 0);
133 | invalidate();
134 | break;
135 | }
136 | return super.onTouchEvent(event);
137 | }
138 |
139 | @Override
140 | public void computeScroll() {
141 | // 第三步,重写computeScroll()方法,并在其内部完成平滑滚动的逻辑
142 | if (mScroller.computeScrollOffset()) {
143 | scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
144 | invalidate();
145 | }
146 | }
147 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zyyoona7/customviewsets/custom_layout_manager/CardActivity.java:
--------------------------------------------------------------------------------
1 | package com.zyyoona7.customviewsets.custom_layout_manager;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.app.AppCompatActivity;
5 | import android.support.v7.widget.RecyclerView;
6 | import android.view.LayoutInflater;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 | import android.widget.TextView;
10 | import android.widget.Toast;
11 |
12 | import com.zyyoona7.customviewsets.R;
13 |
14 | import java.util.Random;
15 |
16 | public class CardActivity extends AppCompatActivity {
17 |
18 | private static final int[] COLORS = {0xff00FFFF, 0xffDEB887, 0xff5F9EA0,
19 | 0xff7FFF00, 0xff6495ED, 0xffDC143C,
20 | 0xff008B8B, 0xff006400, 0xff2F4F4F,
21 | 0xffFF69B4, 0xffFF00FF, 0xffCD5C5C,
22 | 0xff90EE90, 0xff87CEFA, 0xff800000};
23 |
24 | private RecyclerView mRecyclerView;
25 | private Adapter mAdapter = new Adapter();
26 |
27 | private int mCount = 5000;
28 | private int mGroupSize = 3;
29 |
30 | @Override
31 | protected void onCreate(Bundle savedInstanceState) {
32 | super.onCreate(savedInstanceState);
33 | setContentView(R.layout.activity_card);
34 | mRecyclerView = (RecyclerView) findViewById(R.id.list);
35 | init();
36 | }
37 |
38 | private void init() {
39 | mRecyclerView.setLayoutManager(new CardLayoutManager(mGroupSize, true));
40 | mRecyclerView.setAdapter(mAdapter);
41 | }
42 |
43 | public void add(View view) {
44 | mCount += 10;
45 | mAdapter.notifyDataSetChanged();
46 | }
47 |
48 | public void change(View view) {
49 | if (mGroupSize == 3) {
50 | mGroupSize = 9;
51 | } else {
52 | mGroupSize = 3;
53 | }
54 | init();
55 | }
56 |
57 | class Adapter extends RecyclerView.Adapter {
58 |
59 | @Override
60 | public Holder onCreateViewHolder(ViewGroup parent, int viewType) {
61 | View item = LayoutInflater.from(parent.getContext())
62 | .inflate(R.layout.item, parent, false);
63 | return new Holder(item);
64 | }
65 |
66 | @Override
67 | public void onBindViewHolder(final Holder holder, final int position) {
68 | // holder.item.setText("" + position);
69 | holder.item.setCardColor(randomColor());
70 | holder.text.setText("菜单" + position);
71 | holder.itemView.setOnClickListener(new View.OnClickListener() {
72 | @Override
73 | public void onClick(View view) {
74 | Toast.makeText(CardActivity.this, holder.text.getText(), Toast.LENGTH_SHORT).show();
75 | }
76 | });
77 | }
78 |
79 | @Override
80 | public int getItemCount() {
81 | return mCount;
82 | }
83 |
84 | private int randomColor() {
85 | return COLORS[new Random().nextInt(COLORS.length)];
86 | }
87 |
88 | class Holder extends RecyclerView.ViewHolder {
89 | CardItemView item;
90 | TextView text;
91 |
92 | public Holder(View itemView) {
93 | super(itemView);
94 | item = (CardItemView) itemView.findViewById(R.id.item);
95 | text = (TextView) itemView.findViewById(R.id.text);
96 | }
97 | }
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zyyoona7/customviewsets/custom_layout_manager/CardItemView.java:
--------------------------------------------------------------------------------
1 | package com.zyyoona7.customviewsets.custom_layout_manager;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.graphics.Canvas;
6 | import android.graphics.Color;
7 | import android.graphics.Paint;
8 | import android.graphics.Path;
9 | import android.graphics.RectF;
10 | import android.graphics.Region;
11 | import android.util.AttributeSet;
12 | import android.view.MotionEvent;
13 | import android.view.View;
14 |
15 | import com.zyyoona7.customviewsets.R;
16 |
17 | /**
18 | * Created by qibin on 16-9-26.
19 | */
20 |
21 | public class CardItemView extends View {
22 |
23 | private int mSize;
24 | private Paint mPaint;
25 | private Path mDrawPath;
26 | private Region mRegion;
27 |
28 | public CardItemView(Context context) {
29 | this(context, null, 0);
30 | }
31 |
32 | public CardItemView(Context context, AttributeSet attrs) {
33 | this(context, attrs, 0);
34 | }
35 |
36 | public CardItemView(Context context, AttributeSet attrs, int defStyleAttr) {
37 | super(context, attrs, defStyleAttr);
38 | mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
39 | mPaint.setStyle(Paint.Style.FILL);
40 | TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.Card, defStyleAttr, 0);
41 | mSize = ta.getDimensionPixelSize(R.styleable.Card_size, 10);
42 | mPaint.setColor(ta.getColor(R.styleable.Card_bgColor, 0));
43 | ta.recycle();
44 |
45 | mRegion = new Region();
46 | mDrawPath = new Path();
47 |
48 | mDrawPath.moveTo(0, mSize / 2);
49 | mDrawPath.lineTo(mSize / 2, 0);
50 | mDrawPath.lineTo(mSize, mSize / 2);
51 | mDrawPath.lineTo(mSize / 2, mSize);
52 | mDrawPath.close();
53 | }
54 |
55 | @Override
56 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
57 | setMeasuredDimension(mSize, mSize);
58 | }
59 |
60 | @Override
61 | public boolean dispatchTouchEvent(MotionEvent event) {
62 | if (event.getAction() == MotionEvent.ACTION_DOWN) {
63 | if (!isEventInPath(event)) { return false;}
64 | }
65 |
66 | return super.dispatchTouchEvent(event);
67 | }
68 |
69 | private boolean isEventInPath(MotionEvent event) {
70 | RectF bounds = new RectF();
71 | mDrawPath.computeBounds(bounds, true);
72 | mRegion.setPath(mDrawPath, new Region((int)bounds.left,
73 | (int)bounds.top, (int)bounds.right, (int)bounds.bottom));
74 | return mRegion.contains((int) event.getX(), (int) event.getY());
75 | }
76 |
77 | @Override
78 | protected void onDraw(Canvas canvas) {
79 | canvas.drawColor(Color.TRANSPARENT);
80 | canvas.drawPath(mDrawPath, mPaint);
81 | }
82 |
83 | public void setCardColor(int color) {
84 | mPaint.setColor(color);
85 | invalidate();
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zyyoona7/customviewsets/custom_layout_manager/CardLayoutManager.java:
--------------------------------------------------------------------------------
1 | package com.zyyoona7.customviewsets.custom_layout_manager;
2 |
3 | import android.graphics.Rect;
4 | import android.support.v7.widget.RecyclerView;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 |
8 | /**
9 | * Created by qibin on 16-9-25.
10 | */
11 |
12 | public class CardLayoutManager extends RecyclerView.LayoutManager {
13 | public static final int DEFAULT_GROUP_SIZE = 5;
14 |
15 | private int mGroupSize;
16 | private int mHorizontalOffset;
17 | private int mVerticalOffset;
18 | private int mTotalWidth;
19 | private int mTotalHeight;
20 | private int mGravityOffset;
21 | private boolean isGravityCenter;
22 |
23 | private Pool mItemFrames;
24 |
25 | public CardLayoutManager(boolean center) {
26 | this(DEFAULT_GROUP_SIZE, true);
27 | }
28 |
29 | public CardLayoutManager(int groupSize, boolean center) {
30 | mGroupSize = groupSize;
31 | isGravityCenter = center;
32 | mItemFrames = new Pool<>(new Pool.New() {
33 | @Override
34 | public Rect get() { return new Rect();}
35 | });
36 | }
37 |
38 | @Override
39 | public RecyclerView.LayoutParams generateDefaultLayoutParams() {
40 | return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
41 | ViewGroup.LayoutParams.WRAP_CONTENT);
42 | }
43 |
44 | @Override
45 | public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
46 | if (getItemCount() <= 0 || state.isPreLayout()) { return;}
47 |
48 | detachAndScrapAttachedViews(recycler);
49 | View first = recycler.getViewForPosition(0);
50 | measureChildWithMargins(first, 0, 0);
51 | int itemWidth = getDecoratedMeasuredWidth(first);
52 | int itemHeight = getDecoratedMeasuredHeight(first);
53 |
54 | int firstLineSize = mGroupSize / 2 + 1;
55 | int secondLineSize = firstLineSize + mGroupSize / 2;
56 | if (isGravityCenter && firstLineSize * itemWidth < getHorizontalSpace()) {
57 | mGravityOffset = (getHorizontalSpace() - firstLineSize * itemWidth) / 2;
58 | } else {
59 | mGravityOffset = 0;
60 | }
61 |
62 | for (int i = 0; i < getItemCount(); i++) {
63 | Rect item = mItemFrames.get(i);
64 | float coefficient = isFirstGroup(i) ? 1.5f : 1.f;
65 | int offsetHeight = (int) ((i / mGroupSize) * itemHeight * coefficient);
66 |
67 | // 每一组的第一行
68 | if (isItemInFirstLine(i)) {
69 | int offsetInLine = i < firstLineSize ? i : i % mGroupSize;
70 | item.set(mGravityOffset + offsetInLine * itemWidth, offsetHeight, mGravityOffset + offsetInLine * itemWidth + itemWidth,
71 | itemHeight + offsetHeight);
72 | }else {
73 | int lineOffset = itemHeight / 2;
74 | int offsetInLine = (i < secondLineSize ? i : i % mGroupSize) - firstLineSize;
75 | item.set(mGravityOffset + offsetInLine * itemWidth + itemWidth / 2,
76 | offsetHeight + lineOffset, mGravityOffset + offsetInLine * itemWidth + itemWidth + itemWidth / 2,
77 | itemHeight + offsetHeight + lineOffset);
78 | }
79 | }
80 |
81 | mTotalWidth = Math.max(firstLineSize * itemWidth, getHorizontalSpace());
82 | int totalHeight = getGroupSize() * itemHeight;
83 | if (!isItemInFirstLine(getItemCount() - 1)) { totalHeight += itemHeight / 2;}
84 | mTotalHeight = Math.max(totalHeight, getVerticalSpace());
85 | fill(recycler, state);
86 | }
87 |
88 | private void fill(RecyclerView.Recycler recycler, RecyclerView.State state) {
89 | if (getItemCount() <= 0 || state.isPreLayout()) { return;}
90 | Rect displayRect = new Rect(mHorizontalOffset, mVerticalOffset,
91 | getHorizontalSpace() + mHorizontalOffset,
92 | getVerticalSpace() + mVerticalOffset);
93 |
94 | Rect rect = new Rect();
95 | for (int i = 0; i < getChildCount(); i++) {
96 | View item = getChildAt(i);
97 | rect.left = getDecoratedLeft(item);
98 | rect.top = getDecoratedTop(item);
99 | rect.right = getDecoratedRight(item);
100 | rect.bottom = getDecoratedBottom(item);
101 | if (!Rect.intersects(displayRect, rect)) {
102 | removeAndRecycleView(item, recycler);
103 | }
104 | }
105 |
106 | for (int i = 0; i < getItemCount(); i++) {
107 | Rect frame = mItemFrames.get(i);
108 | if (Rect.intersects(displayRect, frame)) {
109 | View scrap = recycler.getViewForPosition(i);
110 | addView(scrap);
111 | measureChildWithMargins(scrap, 0, 0);
112 | layoutDecorated(scrap, frame.left - mHorizontalOffset, frame.top - mVerticalOffset,
113 | frame.right - mHorizontalOffset, frame.bottom - mVerticalOffset);
114 | }
115 | }
116 | }
117 |
118 | @Override
119 | public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
120 | detachAndScrapAttachedViews(recycler);
121 | if (mVerticalOffset + dy < 0) {
122 | dy = -mVerticalOffset;
123 | } else if (mVerticalOffset + dy > mTotalHeight - getVerticalSpace()) {
124 | dy = mTotalHeight - getVerticalSpace() - mVerticalOffset;
125 | }
126 |
127 | offsetChildrenVertical(-dy);
128 | fill(recycler, state);
129 | mVerticalOffset += dy;
130 | return dy;
131 | }
132 |
133 | @Override
134 | public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
135 | detachAndScrapAttachedViews(recycler);
136 | if (mHorizontalOffset + dx < 0) {
137 | dx = -mHorizontalOffset;
138 | } else if (mHorizontalOffset + dx > mTotalWidth - getHorizontalSpace()) {
139 | dx = mTotalWidth - getHorizontalSpace() - mHorizontalOffset;
140 | }
141 |
142 | offsetChildrenHorizontal(-dx);
143 | fill(recycler, state);
144 | mHorizontalOffset += dx;
145 | return dx;
146 | }
147 |
148 | @Override
149 | public boolean canScrollVertically() {
150 | return true;
151 | }
152 |
153 | @Override
154 | public boolean canScrollHorizontally() {
155 | return true;
156 | }
157 |
158 | private boolean isItemInFirstLine(int index) {
159 | int firstLineSize = mGroupSize / 2 + 1;
160 | return index < firstLineSize || (index >= mGroupSize && index % mGroupSize < firstLineSize);
161 | }
162 |
163 | private int getGroupSize() {
164 | return (int) Math.ceil(getItemCount() / (float)mGroupSize);
165 | }
166 |
167 | private boolean isFirstGroup(int index) {
168 | return index < mGroupSize;
169 | }
170 |
171 | private int getHorizontalSpace() {
172 | return getWidth() - getPaddingLeft() - getPaddingRight();
173 | }
174 |
175 | private int getVerticalSpace() {
176 | return getHeight() - getPaddingTop() - getPaddingBottom();
177 | }
178 | }
179 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zyyoona7/customviewsets/custom_layout_manager/Pool.java:
--------------------------------------------------------------------------------
1 | package com.zyyoona7.customviewsets.custom_layout_manager;
2 |
3 | import android.support.v4.util.SparseArrayCompat;
4 |
5 | /**
6 | * Created by qibin on 16-9-25.
7 | */
8 |
9 | public class Pool {
10 | private SparseArrayCompat mPool;
11 | private New mNewInstance;
12 |
13 | public Pool(New newInstance) {
14 | mPool = new SparseArrayCompat<>();
15 | mNewInstance = newInstance;
16 | }
17 |
18 | public T get(int key) {
19 | T res = mPool.get(key);
20 | if (res == null) {
21 | res = mNewInstance.get();
22 | mPool.put(key, res);
23 | }
24 | return res;
25 | }
26 |
27 | public interface New {
28 | T get();
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zyyoona7/customviewsets/heart/Heart.java:
--------------------------------------------------------------------------------
1 | package com.zyyoona7.customviewsets.heart;
2 |
3 | /**
4 | * Created by zyyoona7 on 2017/7/4.
5 | */
6 |
7 | public class Heart {
8 |
9 | public static final int DEFAULT = 0;
10 | public static final int PINK = 1;
11 | public static final int CYAN = 2;
12 | public static final int GREEN = 3;
13 | public static final int YELLOW = 4;
14 | public static final int BLUE = 5;
15 |
16 | //实时坐标
17 | private float x;
18 | private float y;
19 |
20 | //起始点坐标
21 | private float startX;
22 | private float startY;
23 |
24 | //结束点坐标
25 | private float endX;
26 | private float endY;
27 |
28 | //三阶贝塞尔曲线(两个控制点)
29 | //控制点1坐标
30 | private float control1X;
31 | private float control1Y;
32 |
33 | //控制点2坐标
34 | private float control2X;
35 | private float control2Y;
36 |
37 | //实时的时间
38 | private float t=0;
39 | //速率
40 | private float speed;
41 |
42 | //bitmap图片类型
43 | private int type=DEFAULT;
44 |
45 | public float getX() {
46 | return x;
47 | }
48 |
49 | public void setX(float x) {
50 | this.x = x;
51 | }
52 |
53 | public float getY() {
54 | return y;
55 | }
56 |
57 | public void setY(float y) {
58 | this.y = y;
59 | }
60 |
61 | public float getStartX() {
62 | return startX;
63 | }
64 |
65 | public void setStartX(float startX) {
66 | this.startX = startX;
67 | }
68 |
69 | public float getStartY() {
70 | return startY;
71 | }
72 |
73 | public void setStartY(float startY) {
74 | this.startY = startY;
75 | }
76 |
77 | public float getEndX() {
78 | return endX;
79 | }
80 |
81 | public void setEndX(float endX) {
82 | this.endX = endX;
83 | }
84 |
85 | public float getEndY() {
86 | return endY;
87 | }
88 |
89 | public void setEndY(float endY) {
90 | this.endY = endY;
91 | }
92 |
93 | public float getControl1X() {
94 | return control1X;
95 | }
96 |
97 | public void setControl1X(float control1X) {
98 | this.control1X = control1X;
99 | }
100 |
101 | public float getControl1Y() {
102 | return control1Y;
103 | }
104 |
105 | public void setControl1Y(float control1Y) {
106 | this.control1Y = control1Y;
107 | }
108 |
109 | public float getControl2X() {
110 | return control2X;
111 | }
112 |
113 | public void setControl2X(float control2X) {
114 | this.control2X = control2X;
115 | }
116 |
117 | public float getControl2Y() {
118 | return control2Y;
119 | }
120 |
121 | public void setControl2Y(float control2Y) {
122 | this.control2Y = control2Y;
123 | }
124 |
125 | public float getT() {
126 | return t;
127 | }
128 |
129 | public void setT(float t) {
130 | this.t = t;
131 | }
132 |
133 | public float getSpeed() {
134 | return speed;
135 | }
136 |
137 | public void setSpeed(float speed) {
138 | this.speed = speed;
139 | }
140 |
141 | public int getType() {
142 | return type;
143 | }
144 |
145 | public void setType(int type) {
146 | this.type = type;
147 | }
148 |
149 | /**
150 | * 重置下x,y坐标
151 | * 位置在最底部的中间
152 | *
153 | * @param x
154 | * @param y
155 | */
156 | public void initXY(float x, float y) {
157 | this.x = x;
158 | this.y = y;
159 | }
160 |
161 | /**
162 | * 重置起始点和结束点
163 | *
164 | * @param width
165 | * @param height
166 | */
167 | public void initStartAndEnd(float width, float height) {
168 | //起始点和结束点为view的正下方和正上方
169 | this.startX = width / 2;
170 | this.startY = height;
171 | this.endX = width / 2;
172 | this.endY = 0;
173 | initXY(startX,startY);
174 | }
175 |
176 | /**
177 | * 重置控制点坐标
178 | *
179 | * @param width
180 | * @param height
181 | */
182 | public void initControl(float width, float height) {
183 | //随机生成控制点1
184 | this.control1X = (float) (Math.random() * width);
185 | this.control1Y = (float) (Math.random() * height);
186 |
187 | //随机生成控制点2
188 | this.control2X = (float) (Math.random() * width);
189 | this.control2Y = (float) (Math.random() * height);
190 |
191 | //如果两个点重合,重新生成控制点
192 | if (this.control1X == this.control2X && this.control1Y == this.control2Y) {
193 | initControl(width, height);
194 | }
195 | }
196 |
197 | /**
198 | * 重置速率
199 | */
200 | public void initSpeed() {
201 | //随机速率
202 | this.speed = (float) (Math.random() * 0.01 + 0.003);
203 | }
204 |
205 | // /**
206 | // * 重置时间
207 | // * t=0-1;
208 | // */
209 | // public void resetT() {
210 | // this.t = 0;
211 | // }
212 | }
213 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zyyoona7/customviewsets/heart/HeartActivity.java:
--------------------------------------------------------------------------------
1 | package com.zyyoona7.customviewsets.heart;
2 |
3 | import android.support.v7.app.AppCompatActivity;
4 | import android.os.Bundle;
5 | import android.view.View;
6 |
7 | import com.zyyoona7.customviewsets.R;
8 |
9 | import java.util.Random;
10 |
11 | public class HeartActivity extends AppCompatActivity {
12 |
13 | @Override
14 | protected void onCreate(Bundle savedInstanceState) {
15 | super.onCreate(savedInstanceState);
16 | setContentView(R.layout.activity_heart);
17 | final HeartView heartView= (HeartView) findViewById(R.id.heart_view);
18 | heartView.setOnClickListener(new View.OnClickListener() {
19 | @Override
20 | public void onClick(View v) {
21 | heartView.addHeart(new Random().nextInt(6));
22 | }
23 | });
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zyyoona7/customviewsets/heart/HeartView.java:
--------------------------------------------------------------------------------
1 | package com.zyyoona7.customviewsets.heart;
2 |
3 | import android.content.Context;
4 | import android.graphics.Bitmap;
5 | import android.graphics.BitmapFactory;
6 | import android.graphics.Canvas;
7 | import android.graphics.Color;
8 | import android.graphics.Matrix;
9 | import android.graphics.Paint;
10 | import android.graphics.PixelFormat;
11 | import android.graphics.PorterDuff;
12 | import android.util.AttributeSet;
13 | import android.util.Log;
14 | import android.util.SparseArray;
15 | import android.view.SurfaceHolder;
16 | import android.view.SurfaceView;
17 |
18 | import com.zyyoona7.customviewsets.R;
19 |
20 | import java.util.concurrent.ConcurrentLinkedQueue;
21 |
22 | /**
23 | * Created by zyyoona7 on 2017/7/3.
24 | * 直播平台进入直播间点亮功能的心形view
25 | */
26 |
27 | public class HeartView extends SurfaceView implements SurfaceHolder.Callback, Runnable {
28 |
29 | private static final String TAG = "HeartView";
30 |
31 | private boolean isRunning = false;
32 | private Paint mPaint;
33 |
34 | private ConcurrentLinkedQueue mHearts = null;
35 |
36 | private float mWidth;
37 | private float mHeight;
38 |
39 | private Matrix mMatrix;
40 |
41 | private SparseArray mBitmapSparseArray;
42 |
43 | public HeartView(Context context) {
44 | this(context, null);
45 | }
46 |
47 | public HeartView(Context context, AttributeSet attrs) {
48 | super(context, attrs);
49 | init(context);
50 | }
51 |
52 | private void init(Context context) {
53 | mPaint = new Paint();
54 | mPaint.setAntiAlias(true);
55 | mPaint.setColor(Color.RED);
56 | mPaint.setStyle(Paint.Style.STROKE);
57 | mPaint.setStrokeWidth(10);
58 | getHolder().addCallback(this);
59 | //设置背景透明
60 | setZOrderOnTop(true);
61 | getHolder().setFormat(PixelFormat.TRANSLUCENT);
62 | //----
63 | setFocusable(true);
64 | setKeepScreenOn(true);
65 | setFocusableInTouchMode(true);
66 |
67 | mHearts = new ConcurrentLinkedQueue<>();
68 | mMatrix = new Matrix();
69 | mBitmapSparseArray = new SparseArray<>();
70 | Bitmap bitmap1 = BitmapFactory.decodeResource(context.getResources(), R.drawable.heart_default);
71 | Bitmap bitmap2 = BitmapFactory.decodeResource(context.getResources(), R.drawable.ss_heart1);
72 | Bitmap bitmap3 = BitmapFactory.decodeResource(context.getResources(), R.drawable.ss_heart2);
73 | Bitmap bitmap4 = BitmapFactory.decodeResource(context.getResources(), R.drawable.ss_heart3);
74 | Bitmap bitmap5 = BitmapFactory.decodeResource(context.getResources(), R.drawable.ss_heart4);
75 | Bitmap bitmap6 = BitmapFactory.decodeResource(context.getResources(), R.drawable.ss_heart5);
76 | mBitmapSparseArray.put(Heart.DEFAULT, bitmap1);
77 | mBitmapSparseArray.put(Heart.PINK, bitmap2);
78 | mBitmapSparseArray.put(Heart.CYAN, bitmap3);
79 | mBitmapSparseArray.put(Heart.GREEN, bitmap4);
80 | mBitmapSparseArray.put(Heart.YELLOW, bitmap5);
81 | mBitmapSparseArray.put(Heart.BLUE, bitmap6);
82 | }
83 |
84 | @Override
85 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
86 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
87 | }
88 |
89 | @Override
90 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
91 | super.onSizeChanged(w, h, oldw, oldh);
92 | mWidth = getWidth();
93 | mHeight = getHeight();
94 | }
95 |
96 | @Override
97 | public void surfaceCreated(SurfaceHolder holder) {
98 | isRunning = true;
99 | new Thread(this).start();
100 | }
101 |
102 | @Override
103 | public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
104 | }
105 |
106 | @Override
107 | public void surfaceDestroyed(SurfaceHolder holder) {
108 | isRunning = false;
109 | //回收bitmap
110 | for (int i = 0; i < mBitmapSparseArray.size(); i++) {
111 | if (mBitmapSparseArray.valueAt(i) != null) {
112 | mBitmapSparseArray.valueAt(i).recycle();
113 | }
114 | }
115 | }
116 |
117 | @Override
118 | public void run() {
119 | while (isRunning) {
120 | Canvas canvas = null;
121 | try {
122 | canvas = getHolder().lockCanvas();
123 | if (canvas != null) {
124 | //清屏~
125 | canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
126 | for (Heart heart : mHearts) {
127 | if (mBitmapSparseArray.get(heart.getType()) == null) {
128 | continue;
129 | }
130 | mMatrix.setTranslate(0, 0);
131 | //位移到x,y
132 | mMatrix.postTranslate(heart.getX(), heart.getY());
133 | //缩放
134 | // mMatrix.postScale()
135 | //画bitmap
136 | canvas.drawBitmap(mBitmapSparseArray.get(heart.getType()), mMatrix, mPaint);
137 | //计算时间
138 | if (heart.getT() < 1) {
139 | heart.setT(heart.getT() + heart.getSpeed());
140 | handleBezierXY(heart);
141 | } else {
142 | removeHeart(heart);
143 | }
144 | }
145 | }
146 | } catch (Exception e) {
147 | Log.e(TAG, "run: " + e.getMessage());
148 | } finally {
149 | if (canvas != null) {
150 | getHolder().unlockCanvasAndPost(canvas);
151 | }
152 | }
153 |
154 | }
155 | }
156 |
157 | /**
158 | * 计算实时的点坐标
159 | *
160 | * @param heart
161 | */
162 | private void handleBezierXY(Heart heart) {
163 | //三阶贝塞尔曲线函数
164 | //x = (float) (Math.pow((1 - t), 3) * start.x + 3 * t * Math.pow((1 - t), 2) * control1.x + 3 * Math.pow(t, 2) * (1 - t) * control2.x + Math.pow(t, 3) * end.x);
165 | //y = (float) (Math.pow((1 - t), 3) * start.y + 3 * t * Math.pow((1 - t), 2) * control1.y + 3 * Math.pow(t, 2) * (1 - t) * control2.y + Math.pow(t, 3) * end.y);
166 | float x = (float) (Math.pow((1 - heart.getT()), 3) * heart.getStartX() + 3 * heart.getT() * Math.pow((1 - heart.getT()), 2) * heart.getControl1X() + 3 * Math.pow(heart.getT(), 2) * (1 - heart.getT()) * heart.getControl2X() + Math.pow(heart.getT(), 3) * heart.getEndX());
167 | float y = (float) (Math.pow((1 - heart.getT()), 3) * heart.getStartY() + 3 * heart.getT() * Math.pow((1 - heart.getT()), 2) * heart.getControl1Y() + 3 * Math.pow(heart.getT(), 2) * (1 - heart.getT()) * heart.getControl2Y() + Math.pow(heart.getT(), 3) * heart.getEndY());
168 |
169 | heart.setX(x);
170 | heart.setY(y);
171 | }
172 |
173 | /**
174 | * 添加heart
175 | */
176 | public void addHeart() {
177 | Heart heart = new Heart();
178 | initHeart(heart);
179 | mHearts.add(heart);
180 | }
181 |
182 | /**
183 | * 添加heart
184 | *
185 | * @param type
186 | */
187 | public void addHeart(int type) {
188 | Heart heart = new Heart();
189 | initHeart(heart);
190 | heart.setType(type);
191 | mHearts.add(heart);
192 | }
193 |
194 | /**
195 | * 重置
196 | *
197 | * @param heart
198 | */
199 | private void initHeart(Heart heart) {
200 | heart.initStartAndEnd(mWidth, mHeight);
201 | heart.initControl(mWidth, mHeight);
202 | heart.initSpeed();
203 | }
204 |
205 | /**
206 | * 移除Heart
207 | *
208 | * @param heart
209 | */
210 | private void removeHeart(Heart heart) {
211 | mHearts.remove(heart);
212 | }
213 |
214 | }
215 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zyyoona7/customviewsets/pagination_rv/CustomAdapter.java:
--------------------------------------------------------------------------------
1 | package com.zyyoona7.customviewsets.pagination_rv;
2 |
3 | import android.support.v7.widget.RecyclerView;
4 | import android.view.ViewGroup;
5 |
6 | import com.chad.library.adapter.base.BaseQuickAdapter;
7 | import com.chad.library.adapter.base.BaseViewHolder;
8 | import com.zyyoona7.customviewsets.R;
9 |
10 | /**
11 | * Created by zyyoona7 on 2016/9/23.
12 | */
13 |
14 | public class CustomAdapter extends BaseQuickAdapter {
15 |
16 |
17 | public CustomAdapter() {
18 | super(R.layout.item_pagination, null);
19 | }
20 |
21 | @Override
22 | public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
23 | return super.onCreateViewHolder(parent, viewType);
24 | }
25 |
26 | @Override
27 | public void onBindViewHolder(RecyclerView.ViewHolder holder, int positions) {
28 | super.onBindViewHolder(holder, positions);
29 | }
30 |
31 | @Override
32 | protected void convert(BaseViewHolder baseViewHolder, String s) {
33 | baseViewHolder.setText(R.id.tv_item, s);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zyyoona7/customviewsets/pagination_rv/CustomLayoutManager.java:
--------------------------------------------------------------------------------
1 | package com.zyyoona7.customviewsets.pagination_rv;
2 |
3 | import android.graphics.Rect;
4 | import android.support.v7.widget.RecyclerView;
5 | import android.util.Log;
6 | import android.util.SparseArray;
7 | import android.view.View;
8 |
9 | /**
10 | * Created by zyyoona7 on 2016/9/22.
11 | */
12 |
13 | public class CustomLayoutManager extends RecyclerView.LayoutManager {
14 | private static final String TAG = "CustomLayoutManager";
15 |
16 | //保存所有的Item的上下左右的偏移量信息
17 | private SparseArray mAllItemsFrames;
18 |
19 | private int mHorizontalScrollOffset = 0;
20 |
21 | private int mTotalWidth = 0;
22 |
23 | //每页的行列数
24 | private int mSpanColumns = 1;
25 |
26 | private int mSpanRows = 1;
27 |
28 | @Override
29 | public RecyclerView.LayoutParams generateDefaultLayoutParams() {
30 | return new RecyclerView.LayoutParams(RecyclerView.LayoutParams.WRAP_CONTENT, RecyclerView.LayoutParams.WRAP_CONTENT);
31 | }
32 |
33 | @Override
34 | public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
35 | //如果没有item,直接返回
36 | if (getItemCount() <= 0) return;
37 | // 跳过preLayout,preLayout主要用于支持动画
38 | if (state.isPreLayout()) {
39 | return;
40 | }
41 | //在布局之前,将所有的子View先Detach掉,放入到Scrap缓存中
42 | detachAndScrapAttachedViews(recycler);
43 | mAllItemsFrames = new SparseArray<>(getItemCount());
44 | int currentWidth = 0;
45 | int currentHeight = 0;
46 | int currentPage = 1;
47 | int maxCurrentWidth = 0;
48 | View scrap = recycler.getViewForPosition(0);
49 | addView(scrap);
50 | measureChildWithMargins(scrap, 0, 0);
51 | int width = getDecoratedMeasuredWidth(scrap);
52 | int height = getDecoratedMeasuredHeight(scrap);
53 | detachAndScrapView(scrap, recycler);
54 | maxCurrentWidth = (getHorizontalSpace() / width) * width;
55 | Log.e(TAG, "onLayoutChildren: itemCount=" + getItemCount());
56 | for (int i = 0; i < getItemCount(); i++) {
57 | //这里就是从缓存里面取出
58 | // View view = recycler.getViewForPosition(i);
59 | //将View加入到RecyclerView中
60 | // addView(view);
61 | // measureChildWithMargins(view, 0, 0);
62 |
63 | Rect frame = mAllItemsFrames.get(i);
64 | if (frame == null) {
65 | frame = new Rect();
66 | }
67 | if (currentWidth + width > maxCurrentWidth * currentPage) {
68 | currentHeight += height;
69 | currentWidth = maxCurrentWidth * (currentPage - 1);
70 | }
71 |
72 | if (currentHeight + height > getVerticalSpace()) {
73 | currentWidth = maxCurrentWidth * currentPage;
74 | currentHeight = 0;
75 | currentPage++;
76 | }
77 | Log.e(TAG, "onLayoutChildren: currentWidth=" + currentWidth + ",currentHeight=" + currentHeight + ",horizontalSpan=" + getHorizontalSpace() + ",verticalSpace=" + getVerticalSpace() + ",currentPage=" + currentPage + ",maxWidth=" + maxCurrentWidth
78 | );
79 | frame.set(currentWidth, currentHeight, currentWidth + width, currentHeight + height);
80 | // 将当前的Item的Rect边界数据保存
81 | mAllItemsFrames.put(i, frame);
82 |
83 | currentWidth += width;
84 | mTotalWidth = maxCurrentWidth * currentPage;
85 | Log.e(TAG, "onLayoutChildren: totalWidth=" + mTotalWidth);
86 | }
87 |
88 | mTotalWidth = Math.max(mTotalWidth, getHorizontalSpace());
89 | recycleAndFillItems(recycler, state);
90 | }
91 |
92 | /**
93 | * 回收不需要的Item,并且将需要显示的Item从缓存中取出
94 | */
95 | private void recycleAndFillItems(RecyclerView.Recycler recycler, RecyclerView.State state) {
96 | if (state.isPreLayout()) { // 跳过preLayout,preLayout主要用于支持动画
97 | return;
98 | }
99 |
100 | // 当前scroll offset状态下的显示区域
101 | Rect displayFrame = new Rect(mHorizontalScrollOffset, 0, mHorizontalScrollOffset + getHorizontalSpace(), getVerticalSpace());
102 |
103 | /**
104 | * 将滑出屏幕的Items回收到Recycle缓存中
105 | */
106 | Rect childFrame = new Rect();
107 | for (int i = 0; i < getChildCount(); i++) {
108 | View child = getChildAt(i);
109 | childFrame.left = getDecoratedLeft(child);
110 | childFrame.top = getDecoratedTop(child);
111 | childFrame.right = getDecoratedRight(child);
112 | childFrame.bottom = getDecoratedBottom(child);
113 | //如果Item没有在显示区域,就说明需要回收
114 | if (!Rect.intersects(displayFrame, childFrame)) {
115 | //回收掉滑出屏幕的View
116 | removeAndRecycleView(child,recycler);
117 |
118 | }
119 | }
120 |
121 | //重新显示需要出现在屏幕的子View
122 | for (int i = 0; i < getItemCount(); i++) {
123 | if (Rect.intersects(displayFrame, mAllItemsFrames.get(i))) {
124 |
125 | View scrap = recycler.getViewForPosition(i);
126 | measureChildWithMargins(scrap, 0, 0);
127 | addView(scrap);
128 |
129 | Rect frame = mAllItemsFrames.get(i);
130 | //将这个item布局出来
131 | layoutDecorated(scrap,
132 | frame.left - mHorizontalScrollOffset,
133 | frame.top,
134 | frame.right - mHorizontalScrollOffset,
135 | frame.bottom);
136 |
137 | }
138 | }
139 | }
140 |
141 | @Override
142 | public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
143 | //先detach掉所有的子View
144 | detachAndScrapAttachedViews(recycler);
145 |
146 | //实际要滑动的距离
147 | int travel = dx;
148 | //
149 | // 如果滑动到最左面
150 | if (mHorizontalScrollOffset + dx < 0) {
151 | travel = -mHorizontalScrollOffset;
152 | } else if (mHorizontalScrollOffset + dx > mTotalWidth - getHorizontalSpace()) {//如果滑动到最右部
153 | travel = mTotalWidth - getHorizontalSpace() - mHorizontalScrollOffset;
154 | }
155 | //
156 | // 将水平方向的偏移量+travel
157 | mHorizontalScrollOffset += travel;
158 | offsetChildrenHorizontal(-travel);
159 | recycleAndFillItems(recycler, state);
160 | Log.e(TAG, "scrollHorizontallyBy: childCount=" + getChildCount());
161 | return travel;
162 | }
163 |
164 | @Override
165 | public boolean canScrollHorizontally() {
166 | return true;
167 | }
168 |
169 |
170 | private int getVerticalSpace() {
171 | return getHeight() - getPaddingBottom() - getPaddingTop();
172 | }
173 |
174 | private int getHorizontalSpace() {
175 | return getWidth() - getPaddingRight() - getPaddingLeft();
176 | }
177 | }
178 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zyyoona7/customviewsets/pagination_rv/LayoutManagerActivity.java:
--------------------------------------------------------------------------------
1 | package com.zyyoona7.customviewsets.pagination_rv;
2 |
3 | import android.os.Bundle;
4 | import android.os.Handler;
5 | import android.support.v4.view.ViewCompat;
6 | import android.support.v7.widget.GridLayoutManager;
7 | import android.support.v7.widget.LinearLayoutManager;
8 | import android.support.v7.widget.RecyclerView;
9 | import android.util.Log;
10 |
11 | import com.chad.library.adapter.base.BaseQuickAdapter;
12 | import com.zyyoona7.customviewsets.BaseActivity;
13 | import com.zyyoona7.customviewsets.R;
14 |
15 | import java.util.LinkedList;
16 | import java.util.List;
17 |
18 | import butterknife.BindView;
19 |
20 | public class LayoutManagerActivity extends BaseActivity {
21 |
22 | private static final String TAG = "LayoutManagerActivity";
23 |
24 | @BindView(R.id.id_layout_manager_recycler_view)
25 | RecyclerView mRecyclerView;
26 |
27 | int currentVisiblePosition = 0;
28 | int currentLastVisiblePosition = 0;
29 | LinkedList list = new LinkedList<>();
30 |
31 |
32 | @Override
33 | protected void onCreate(Bundle savedInstanceState) {
34 | super.onCreate(savedInstanceState);
35 | mRecyclerView.setLayoutManager(new GridLayoutManager(this, 4, LinearLayoutManager.HORIZONTAL, false));
36 | // mRecyclerView.setLayoutManager(new LinearLayoutManager(this,LinearLayoutManager.HORIZONTAL,false));
37 | final CustomAdapter adapter = new CustomAdapter();
38 | mRecyclerView.setAdapter(adapter);
39 | adapter.setNewData(createData());
40 | // SnapHelper snapHelper = new PaginationSnapHelper(4);
41 | // snapHelper.attachToRecyclerView(mRecyclerView);
42 | mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
43 | @Override
44 | public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
45 | super.onScrollStateChanged(recyclerView, newState);
46 | // Log.e(TAG, "onScrollStateChanged: state=" + newState);
47 | if (RecyclerView.SCROLL_STATE_IDLE == newState) {
48 | // int visiblePosition = ((GridLayoutManager) recyclerView.getLayoutManager()).findFirstVisibleItemPosition();
49 | // int visibleCount = ((GridLayoutManager) recyclerView.getLayoutManager()).getChildCount();
50 | // int itemCount = recyclerView.getLayoutManager().getItemCount();
51 | // if (visibleCount + visiblePosition >= itemCount) {
52 | // Log.e(TAG, "onScrolled: end");
53 | // ((BaseQuickAdapter) recyclerView.getAdapter()).addData(createData());
54 | // }
55 | Log.e(TAG, "onScrollStateChanged: stopped" );
56 | }
57 | }
58 |
59 | @Override
60 | public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
61 | super.onScrolled(recyclerView, dx, dy);
62 | // Log.e(TAG, "onScrolled: dx=" + dx + ",dy=" + dy);
63 | int visiblePosition = ((GridLayoutManager) recyclerView.getLayoutManager()).findFirstVisibleItemPosition();
64 | int lastVisiblePosition = ((GridLayoutManager) recyclerView.getLayoutManager()).findLastVisibleItemPosition();
65 | // Log.e(TAG, "onScrolled:first visible position=" + visiblePosition);
66 | // Log.e(TAG, "onScrolled:last visible position=" + lastVisiblePosition);
67 | // Log.e(TAG, "onScrolled:current visible position=" + currentVisiblePosition);
68 | if (visiblePosition != currentVisiblePosition && visiblePosition > currentVisiblePosition) {
69 | //next page
70 | currentVisiblePosition = visiblePosition;
71 | // currentLastVisiblePosition = visiblePosition + 3;
72 | // onNextPage(currentVisiblePosition);
73 | Log.e(TAG, "onScrolled: next page");
74 | } else if (visiblePosition!= currentVisiblePosition && visiblePosition < currentVisiblePosition) {
75 | //pre page
76 | currentVisiblePosition = visiblePosition;
77 | // currentVisiblePosition = lastVisiblePosition - 3;
78 | // onPrePage(currentVisiblePosition);
79 | Log.e(TAG, "onScrolled: pre page");
80 | }
81 | //最左边
82 | // Log.e(TAG, "onScrolled: can Scroll Horizontal=" + recyclerView.canScrollHorizontally(-1));
83 | if (!recyclerView.canScrollHorizontally(-1)) {
84 | //left refresh
85 | }
86 | //最右边
87 | // Log.e(TAG, "onScrolled: can Scroll Horizontal=" + recyclerView.canScrollHorizontally(1));
88 | if (!recyclerView.canScrollHorizontally(1)) {
89 | //right load more
90 | // int visiblePosition = ((GridLayoutManager) recyclerView.getLayoutManager()).findFirstVisibleItemPosition();
91 | // int visibleCount = ((GridLayoutManager) recyclerView.getLayoutManager()).getChildCount();
92 | // int itemCount = recyclerView.getLayoutManager().getItemCount();
93 | // if (visibleCount + visiblePosition >= itemCount) {
94 | Log.e(TAG, "onScrolled: end");
95 | adapter.addData(createData());
96 | // }
97 | }
98 | }
99 | });
100 | }
101 |
102 | @Override
103 | protected int getContentViewID() {
104 | return R.layout.activity_layout_manager;
105 | }
106 |
107 | private List createData() {
108 | for (int i = 0; i < 50; i++) {
109 | list.add("position=" + i);
110 | }
111 | return list;
112 | }
113 |
114 | private void onNextPage(int currentPosition) {
115 | for (int i = list.size(); i < list.size() + 4; i++) {
116 | list.add("position=" + i);
117 | }
118 |
119 | for (int i = 0; i < 4; i++) {
120 | list.removeFirst();
121 | }
122 | }
123 |
124 | private void onPrePage(int currentPosition) {
125 | for (int i = 0; i < 4; i++) {
126 | list.addFirst("position=" + i);
127 | }
128 |
129 | for (int i = list.size() - 1; i > list.size() - 5; i--) {
130 | list.removeLast();
131 | }
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zyyoona7/customviewsets/pagination_rv/PaginationSnapHelper.java:
--------------------------------------------------------------------------------
1 | package com.zyyoona7.customviewsets.pagination_rv;
2 |
3 | import android.support.v7.widget.LinearSnapHelper;
4 | import android.support.v7.widget.RecyclerView;
5 | import android.view.View;
6 |
7 | /**
8 | * Created by zyyoona7 on 2016/9/29.
9 | *
10 | * code from http://stackoverflow.com/questions/29134094/recyclerview-horizontal-scroll-snap-in-center
11 | */
12 |
13 | public class PaginationSnapHelper extends LinearSnapHelper {
14 |
15 | private static final String TAG = "PaginationSnapHelper";
16 |
17 |
18 | private int mPageSize = 1;
19 |
20 | public PaginationSnapHelper(int pageSize) {
21 | this.mPageSize = pageSize;
22 | }
23 |
24 |
25 | @Override
26 | public int findTargetSnapPosition(RecyclerView.LayoutManager layoutManager, int velocityX, int velocityY) {
27 | View centerView = findSnapView(layoutManager);
28 | if (centerView == null) {
29 | return RecyclerView.NO_POSITION;
30 | }
31 |
32 | int position = layoutManager.getPosition(centerView);
33 | int targetPosition = -1;
34 | if (layoutManager.canScrollHorizontally()) {
35 | if (velocityX < 0) {
36 | targetPosition = position - mPageSize;
37 | } else {
38 | targetPosition = position + mPageSize;
39 | }
40 | }
41 |
42 | if (layoutManager.canScrollVertically()) {
43 | if (velocityY < 0) {
44 | targetPosition = position - mPageSize;
45 | } else {
46 | targetPosition = position + mPageSize;
47 | }
48 | }
49 |
50 | final int firstItem = 0;
51 | final int lastItem = layoutManager.getItemCount() - 1;
52 | targetPosition = Math.min(lastItem, Math.max(targetPosition, firstItem));
53 | return targetPosition;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zyyoona7/customviewsets/pagination_rv/SnappyRecyclerView.java:
--------------------------------------------------------------------------------
1 | package com.zyyoona7.customviewsets.pagination_rv;
2 |
3 | import android.content.Context;
4 | import android.support.annotation.Nullable;
5 | import android.support.v7.widget.LinearLayoutManager;
6 | import android.support.v7.widget.RecyclerView;
7 | import android.util.AttributeSet;
8 | import android.util.Log;
9 | import android.view.View;
10 |
11 | /**
12 | * Created by zyyoona7 on 2016/9/29.
13 | *
14 | * code from http://stackoverflow.com/questions/29134094/recyclerview-horizontal-scroll-snap-in-center
15 | */
16 | public class SnappyRecyclerView extends RecyclerView {
17 | private static final String TAG = "SnappyRecyclerView";
18 |
19 | // Use it with a horizontal LinearLayoutManager
20 | // Based on http://stackoverflow.com/a/29171652/4034572
21 |
22 | public SnappyRecyclerView(Context context) {
23 | super(context);
24 | }
25 |
26 | public SnappyRecyclerView(Context context, @Nullable AttributeSet attrs) {
27 | super(context, attrs);
28 | }
29 |
30 | public SnappyRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
31 | super(context, attrs, defStyle);
32 | }
33 |
34 | @Override
35 | public boolean fling(int velocityX, int velocityY) {
36 | LinearLayoutManager linearLayoutManager = (LinearLayoutManager) getLayoutManager();
37 |
38 | // int screenWidth = Resources.getSystem().getDisplayMetrics().widthPixels;
39 | int screenWidth = getMeasuredWidth();
40 | // views on the screen
41 | int lastVisibleItemPosition = linearLayoutManager.findLastVisibleItemPosition();
42 | View lastView = linearLayoutManager.findViewByPosition(lastVisibleItemPosition);
43 | int firstVisibleItemPosition = linearLayoutManager.findFirstVisibleItemPosition();
44 | View firstView = linearLayoutManager.findViewByPosition(firstVisibleItemPosition);
45 |
46 | // distance we need to scroll
47 | int leftMargin = (screenWidth - lastView.getWidth()) / 2;
48 | int rightMargin = (screenWidth - firstView.getWidth()) / 2 + firstView.getWidth();
49 | int leftEdge = lastView.getLeft();
50 | int rightEdge = firstView.getRight();
51 | int scrollDistanceLeft = leftEdge - leftMargin;
52 | int scrollDistanceRight = rightMargin - rightEdge;
53 |
54 | if (Math.abs(velocityX) < 1000) {
55 | // The fling is slow -> stay at the current page if we are less than half through,
56 | // or go to the next page if more than half through
57 |
58 | if (leftEdge > screenWidth / 2) {
59 | // go to next page
60 | smoothScrollBy(-scrollDistanceRight, 0);
61 | } else if (rightEdge < screenWidth / 2) {
62 | // go to next page
63 | smoothScrollBy(scrollDistanceLeft, 0);
64 | } else {
65 | // stay at current page
66 | if (velocityX > 0) {
67 | smoothScrollBy(-scrollDistanceRight, 0);
68 | } else {
69 | smoothScrollBy(scrollDistanceLeft, 0);
70 | }
71 | }
72 | return true;
73 |
74 | } else {
75 | // The fling is fast -> go to next page
76 |
77 | if (velocityX > 0) {
78 | smoothScrollBy(scrollDistanceLeft, 0);
79 | } else {
80 | smoothScrollBy(-scrollDistanceRight, 0);
81 | }
82 | return true;
83 |
84 | }
85 |
86 | }
87 |
88 | @Override
89 | public void onScrollStateChanged(int state) {
90 | super.onScrollStateChanged(state);
91 |
92 | // If you tap on the phone while the RecyclerView is scrolling it will stop in the middle.
93 | // This code fixes this. This code is not strictly necessary but it improves the behaviour.
94 |
95 | if (state == SCROLL_STATE_IDLE) {
96 | LinearLayoutManager linearLayoutManager = (LinearLayoutManager) getLayoutManager();
97 |
98 | // int screenWidth = Resources.getSystem().getDisplayMetrics().widthPixels;
99 | int screenWidth = getMeasuredWidth();
100 | // views on the screen
101 | int lastVisibleItemPosition = linearLayoutManager.findLastVisibleItemPosition();
102 | View lastView = linearLayoutManager.findViewByPosition(lastVisibleItemPosition);
103 | int firstVisibleItemPosition = linearLayoutManager.findFirstVisibleItemPosition();
104 | View firstView = linearLayoutManager.findViewByPosition(firstVisibleItemPosition);
105 |
106 | // distance we need to scroll
107 | int leftMargin = (screenWidth - lastView.getWidth()) / 2;
108 | int rightMargin = (screenWidth - firstView.getWidth()) / 2 + firstView.getWidth();
109 | int leftEdge = lastView.getLeft();
110 | int rightEdge = firstView.getRight();
111 | int scrollDistanceLeft = leftEdge - leftMargin;
112 | int scrollDistanceRight = rightMargin - rightEdge;
113 |
114 | if (leftEdge > screenWidth / 2) {
115 | smoothScrollBy(-scrollDistanceRight, 0);
116 | } else if (rightEdge < screenWidth / 2) {
117 | smoothScrollBy(scrollDistanceLeft, 0);
118 | }
119 | }
120 | }
121 |
122 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zyyoona7/customviewsets/utils/DensityUtils.java:
--------------------------------------------------------------------------------
1 | package com.zyyoona7.customviewsets.utils;
2 |
3 | import android.content.Context;
4 |
5 | /**
6 | * A utils of screen density.
7 | *
8 | * it can help you convert the different unit.
9 | *
10 | * E.g: dp -> px ps->dp sp->px px->sp
11 | *
12 | * Author: GcsSloop
13 | *
14 | * Created Date: 16/5/24
15 | *
16 | * Copyright (C) 2016 GcsSloop.
17 | *
18 | * GitHub: https://github.com/GcsSloop
19 | */
20 | public class DensityUtils {
21 |
22 | public DensityUtils() {
23 | }
24 |
25 | /**
26 | * convert the dp to px depend on the device density.
27 | *
28 | * @param context the context
29 | * @param dpValue a value of dp
30 | * @return the result of px
31 | */
32 | public static int dip2px(Context context, float dpValue) {
33 | return (int) (dpValue * getDensity(context) + 0.5f);
34 | }
35 |
36 | /**
37 | * convert the px to dp depend on the device density.
38 | *
39 | * @param context the context
40 | * @param pxValue a value of px
41 | * @return the result of dp
42 | */
43 | public static int px2dip(Context context, float pxValue) {
44 | return (int) (pxValue / getDensity(context) + 0.5f);
45 | }
46 |
47 | /**
48 | * convert the sp to px depend on the device scaledDensity.
49 | *
50 | * @param context the context
51 | * @param spValue a value of sp
52 | * @return the result of px
53 | */
54 | public static int sp2px(Context context, float spValue) {
55 | return (int) (spValue * getFontDensity(context) + 0.5);
56 | }
57 |
58 | /**
59 | * convert the px to sp depend on the device scaledDensity.
60 | *
61 | * @param context the context
62 | * @param pxValue a value of px
63 | * @return the result of sp
64 | */
65 | public static int px2sp(Context context, float pxValue) {
66 | return (int) (pxValue / getFontDensity(context) + 0.5);
67 | }
68 |
69 | /**
70 | * get the density of device screen.
71 | *
72 | * @param context the context
73 | * @return the screen density
74 | */
75 | public static float getDensity(Context context) {
76 | return context.getResources().getDisplayMetrics().density;
77 | }
78 |
79 | /**
80 | * get the scale density of device screen.
81 | * usually this value is the same as density.
82 | * but it can adjust by user.
83 | *
84 | * @param context the context
85 | * @return the screen scale density.
86 | */
87 | public static float getFontDensity(Context context) {
88 | return context.getResources().getDisplayMetrics().scaledDensity;
89 | }
90 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zyyoona7/customviewsets/zoom_hover/OnItemSelectedListener.java:
--------------------------------------------------------------------------------
1 | package com.zyyoona7.customviewsets.zoom_hover;
2 |
3 | import android.view.View;
4 |
5 | /**
6 | * item选中回调
7 | */
8 | public interface OnItemSelectedListener {
9 |
10 | /**
11 | * item选中回调方法
12 | *
13 | * @param view
14 | * @param position
15 | */
16 | void onItemSelected(View view, int position);
17 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zyyoona7/customviewsets/zoom_hover/OnZoomAnimatorListener.java:
--------------------------------------------------------------------------------
1 | package com.zyyoona7.customviewsets.zoom_hover;
2 |
3 | import android.view.View;
4 |
5 | /**
6 | * 缩放动画回调
7 | */
8 | public interface OnZoomAnimatorListener {
9 | /**
10 | * 放大动画开始
11 | *
12 | * @param view
13 | */
14 | void onZoomInStart(View view);
15 |
16 | /**
17 | * 放大动画结束
18 | *
19 | * @param view
20 | */
21 | void onZoomInEnd(View view);
22 |
23 | /**
24 | * 缩小动画开始
25 | *
26 | * @param view
27 | */
28 | void onZoomOutStart(View view);
29 |
30 | /**
31 | * 缩小动画结束
32 | *
33 | * @param view
34 | */
35 | void onZoomOutEnd(View view);
36 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zyyoona7/customviewsets/zoom_hover/TestZoomHoverAdapter.java:
--------------------------------------------------------------------------------
1 | package com.zyyoona7.customviewsets.zoom_hover;
2 |
3 | import android.view.LayoutInflater;
4 | import android.view.View;
5 | import android.view.ViewGroup;
6 | import android.widget.TextView;
7 |
8 | import com.zyyoona7.customviewsets.R;
9 |
10 | import java.util.List;
11 |
12 | /**
13 | * Created by User on 2016/8/16.
14 | */
15 |
16 | public class TestZoomHoverAdapter extends ZoomHoverAdapter {
17 |
18 |
19 | public TestZoomHoverAdapter(List list) {
20 | super(list);
21 | }
22 |
23 | @Override
24 | public View getView(ViewGroup parent, int position, String s) {
25 | View contentView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_overlay_card, parent, false);
26 | TextView textView= (TextView) contentView.findViewById(R.id.tv_item);
27 | textView.setText(s);
28 | return contentView;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zyyoona7/customviewsets/zoom_hover/ZoomHoverActivity.java:
--------------------------------------------------------------------------------
1 | package com.zyyoona7.customviewsets.zoom_hover;
2 |
3 | import android.os.Bundle;
4 | import android.support.annotation.Nullable;
5 | import android.support.v4.util.SimpleArrayMap;
6 | import android.view.View;
7 | import android.widget.Toast;
8 |
9 | import com.zyyoona7.customviewsets.BaseActivity;
10 | import com.zyyoona7.customviewsets.R;
11 |
12 | import java.util.ArrayList;
13 | import java.util.List;
14 |
15 | import butterknife.BindView;
16 |
17 | public class ZoomHoverActivity extends BaseActivity {
18 | private static final String TAG = "ZoomHoverActivity";
19 | List mList = new ArrayList<>();
20 | TestZoomHoverAdapter mAdapter;
21 | @BindView(R.id.zoom_hover_grid_view)
22 | ZoomHoverGridView mZoomHoverGridView;
23 | @BindView(R.id.zoom_hover_view)
24 | ZoomHoverView mZoomHoverView;
25 |
26 | @Override
27 | protected int getContentViewID() {
28 | return R.layout.activity_zoom_hover;
29 | }
30 |
31 | @Override
32 | protected void onCreate(@Nullable Bundle savedInstanceState) {
33 | super.onCreate(savedInstanceState);
34 | mList.add("test 1");
35 | mList.add("test 2");
36 | mList.add("test 3");
37 | mList.add("test 4");
38 | mList.add("test 5");
39 | mList.add("test 6");
40 | mList.add("test 7");
41 | mList.add("test 8");
42 | mList.add("test 9");
43 | mList.add("test 10");
44 | mAdapter = new TestZoomHoverAdapter(mList);
45 | final SimpleArrayMap map = new SimpleArrayMap<>();
46 | map.put(0, 2);
47 | mZoomHoverView.setSpan(map);
48 | mZoomHoverView.setAdapter(mAdapter);
49 |
50 | mZoomHoverView.setOnZoomAnimatorListener(new OnZoomAnimatorListener() {
51 | @Override
52 | public void onZoomInStart(View view) {
53 | view.setBackground(getResources().getDrawable(android.R.drawable.dialog_holo_light_frame));
54 | }
55 |
56 | @Override
57 | public void onZoomInEnd(View view) {
58 |
59 | }
60 |
61 | @Override
62 | public void onZoomOutStart(View view) {
63 |
64 | }
65 |
66 | @Override
67 | public void onZoomOutEnd(View view) {
68 | view.setBackgroundColor(getResources().getColor(R.color.colorAccent));
69 | }
70 | });
71 | mZoomHoverGridView.setOnZoomAnimatorListener(new OnZoomAnimatorListener() {
72 | @Override
73 | public void onZoomInStart(View view) {
74 | view.setBackground(getResources().getDrawable(android.R.drawable.dialog_holo_light_frame));
75 | }
76 |
77 | @Override
78 | public void onZoomInEnd(View view) {
79 |
80 | }
81 |
82 | @Override
83 | public void onZoomOutStart(View view) {
84 |
85 | }
86 |
87 | @Override
88 | public void onZoomOutEnd(View view) {
89 | view.setBackgroundColor(getResources().getColor(R.color.colorAccent));
90 | }
91 | });
92 |
93 | mZoomHoverView.setOnItemSelectedListener(new OnItemSelectedListener() {
94 | @Override
95 | public void onItemSelected(View view, int position) {
96 | Toast.makeText(ZoomHoverActivity.this, "selected position=" + position, Toast.LENGTH_SHORT).show();
97 | }
98 | });
99 |
100 | mZoomHoverView.postDelayed(new Runnable() {
101 | @Override
102 | public void run() {
103 | mZoomHoverView.setSelectedItem(1);
104 | }
105 | }, 2000);
106 |
107 | mZoomHoverView.postDelayed(new Runnable() {
108 | @Override
109 | public void run() {
110 | mZoomHoverView.setSelectedItem(3);
111 | }
112 | }, 3000);
113 |
114 | mZoomHoverView.postDelayed(new Runnable() {
115 | @Override
116 | public void run() {
117 | mZoomHoverView.setSelectedItem(0);
118 | }
119 | }, 4000);
120 | //
121 | mZoomHoverGridView.addSpanItem(0, 1, 2);
122 | mZoomHoverGridView.addSpanItem(3, 2, 2);
123 | mZoomHoverGridView.addSpanItem(4, 2, 1);
124 | mZoomHoverGridView.addSpanItem(5, 1, 3);
125 |
126 | mZoomHoverGridView.setAdapter(mAdapter);
127 | }
128 |
129 | @Override
130 | protected void onResume() {
131 | super.onResume();
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zyyoona7/customviewsets/zoom_hover/ZoomHoverAdapter.java:
--------------------------------------------------------------------------------
1 | package com.zyyoona7.customviewsets.zoom_hover;
2 |
3 | import android.view.View;
4 | import android.view.ViewGroup;
5 |
6 | import java.util.ArrayList;
7 | import java.util.Arrays;
8 | import java.util.List;
9 |
10 | /**
11 | * Created by zyyoona7 on 2016/8/16.
12 | */
13 |
14 | public abstract class ZoomHoverAdapter {
15 |
16 | private List mDataList;
17 |
18 | //数据变化回调
19 | private OnDataChangedListener mOnDataChangedListener;
20 |
21 |
22 | /**
23 | * 数据变化回调
24 | */
25 | interface OnDataChangedListener {
26 | void onChanged();
27 | }
28 |
29 |
30 | public ZoomHoverAdapter(List list) {
31 | this.mDataList = list;
32 | }
33 |
34 | public ZoomHoverAdapter(T[] datas) {
35 | this.mDataList = new ArrayList<>(Arrays.asList(datas));
36 | }
37 |
38 | /**
39 | * 设置数据变化监听器
40 | *
41 | * @param listener
42 | */
43 | public void setDataChangedListener(OnDataChangedListener listener) {
44 | this.mOnDataChangedListener = listener;
45 | }
46 |
47 | /**
48 | * 通知adapter数据发生变化
49 | */
50 | public void notifyDataChanged() {
51 | if (this.mOnDataChangedListener != null) {
52 | this.mOnDataChangedListener.onChanged();
53 | }
54 | }
55 |
56 | /**
57 | * 获取数据的总数
58 | *
59 | * @return
60 | */
61 | public int getCount() {
62 | return mDataList == null ? 0 : mDataList.size();
63 | }
64 |
65 | /**
66 | * 获取对应Item的bean
67 | *
68 | * @param position
69 | * @return
70 | */
71 | public T getItem(int position) {
72 | if (mDataList == null) {
73 | return null;
74 | } else {
75 | return mDataList.get(position);
76 | }
77 | }
78 |
79 | public abstract View getView(ViewGroup parent, int position, T t);
80 |
81 | }
82 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zyyoona7/customviewsets/zoom_hover/ZoomHoverGridView.java:
--------------------------------------------------------------------------------
1 | package com.zyyoona7.customviewsets.zoom_hover;
2 |
3 | import android.animation.Animator;
4 | import android.animation.AnimatorSet;
5 | import android.animation.ObjectAnimator;
6 | import android.content.Context;
7 | import android.content.res.TypedArray;
8 | import android.graphics.Canvas;
9 | import android.os.Build;
10 | import android.support.v4.util.SimpleArrayMap;
11 | import android.util.AttributeSet;
12 | import android.view.View;
13 | import android.view.animation.AccelerateDecelerateInterpolator;
14 | import android.view.animation.Interpolator;
15 | import android.widget.GridLayout;
16 |
17 | import com.zyyoona7.customviewsets.R;
18 | import com.zyyoona7.customviewsets.utils.DensityUtils;
19 |
20 | import java.util.ArrayList;
21 | import java.util.HashMap;
22 | import java.util.List;
23 | import java.util.Set;
24 |
25 | import static android.os.Build.VERSION_CODES.KITKAT;
26 |
27 | /**
28 | * Created by zyyoona7 on 2016/8/29.
29 | */
30 |
31 | public class ZoomHoverGridView extends GridLayout implements View.OnClickListener, ZoomHoverAdapter.OnDataChangedListener {
32 |
33 | private static final String TAG = "ZoomHoverGridView";
34 |
35 | //adapter
36 | private ZoomHoverAdapter mZoomHoverAdapter;
37 |
38 | // 需要的列数
39 | private int mColumnNum = 3;
40 |
41 | // 需要的行数
42 | private int mRowNum = 3;
43 |
44 | //子view距离父控件的外边距宽度
45 | private int mMarginParent = 20;
46 |
47 | //行列的分割线宽度
48 | private int mDivider = 10;
49 |
50 | //当前放大动画
51 | private AnimatorSet mCurrentZoomInAnim = null;
52 |
53 | //当前缩小动画
54 | private AnimatorSet mCurrentZoomOutAnim = null;
55 |
56 | //缩放动画监听器
57 | private OnZoomAnimatorListener mOnZoomAnimatorListener = null;
58 |
59 | //动画持续时间
60 | private int mAnimDuration;
61 |
62 | //动画缩放倍数
63 | private float mAnimZoomTo;
64 |
65 | //缩放动画插值器
66 | private Interpolator mZoomInInterpolator;
67 | private Interpolator mZoomOutInterpolator;
68 |
69 | //上一个ZoomOut的view(为了解决快速切换时,上一个被缩小的view缩放大小不正常的情况)
70 | private View mPreZoomOutView;
71 |
72 | //当前被选中的view
73 | private View mCurrentView = null;
74 |
75 | //item选中监听器
76 | private OnItemSelectedListener mOnItemSelectedListener;
77 |
78 | //存储当前layout中所有子view
79 | private List mViewList = new ArrayList<>();
80 |
81 | //存储Span信息的list
82 | private SimpleArrayMap mSpanMap = new SimpleArrayMap<>();
83 |
84 | //存储每个item
85 | private List mItemList = new ArrayList<>();
86 |
87 | //矩阵,用来存储位置信息
88 | private boolean mMatrix[][];
89 |
90 | //基准的宽高
91 | private int mBaseWidth = 0;
92 | private int mBaseHeight = 0;
93 | //是否使用基准宽高
94 | private boolean mUseBaseWH = false;
95 |
96 | //确定位置时起始地行列值,防止每次从0开始循环
97 | private int mStartColumn = 0;
98 | private int mStartRow = 0;
99 |
100 | public ZoomHoverGridView(Context context) {
101 | this(context, null);
102 | }
103 |
104 | public ZoomHoverGridView(Context context, AttributeSet attrs) {
105 | super(context, attrs);
106 | TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ZoomHoverView);
107 | mDivider = typedArray.getDimensionPixelOffset(R.styleable.ZoomHoverView_zhv_divider, DensityUtils.dip2px(context, 5));
108 | mMarginParent = typedArray.getDimensionPixelOffset(R.styleable.ZoomHoverView_zhv_margin_parent, DensityUtils.dip2px(context, 5));
109 | mColumnNum = typedArray.getInt(R.styleable.ZoomHoverView_zhv_column_num, 3);
110 | mAnimDuration = typedArray.getInt(R.styleable.ZoomHoverView_zhv_zoom_duration, getResources().getInteger(android.R.integer.config_shortAnimTime));
111 | mAnimZoomTo = typedArray.getFloat(R.styleable.ZoomHoverView_zhv_zoom_to, 1.2f);
112 | mUseBaseWH = typedArray.getBoolean(R.styleable.ZoomHoverView_zhv_use_baseWH, false);
113 | mBaseWidth = typedArray.getDimensionPixelSize(R.styleable.ZoomHoverView_zhv_base_width, DensityUtils.dip2px(context, 50));
114 | mBaseHeight = typedArray.getDimensionPixelSize(R.styleable.ZoomHoverView_zhv_base_height, DensityUtils.dip2px(context, 50));
115 | typedArray.recycle();
116 | mZoomOutInterpolator = mZoomInInterpolator = new AccelerateDecelerateInterpolator();
117 | }
118 |
119 | @Override
120 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
121 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
122 |
123 | }
124 |
125 | @Override
126 | protected void onDraw(Canvas canvas) {
127 | super.onDraw(canvas);
128 | }
129 |
130 | /**
131 | * 设置适配器
132 | *
133 | * @param adapter
134 | */
135 |
136 | public void setAdapter(ZoomHoverAdapter adapter) {
137 | this.mZoomHoverAdapter = adapter;
138 | mZoomHoverAdapter.setDataChangedListener(this);
139 | changeAdapter();
140 | }
141 |
142 |
143 | /**
144 | * 根据adapter添加view
145 | */
146 | private void changeAdapter() {
147 | //移除所有view
148 | removeAllViews();
149 | //计算子view并保存到list中
150 | calculateChild();
151 | //计算每个view的下标并设置位置
152 | calculatePosition();
153 | //计算子view的实际位置
154 | calculatePositionExact();
155 | //添加view
156 | addItem();
157 | }
158 |
159 | /**
160 | * 计算子view的个数并添加到list中
161 | */
162 | private void calculateChild() {
163 | mViewList.clear();
164 | mItemList.clear();
165 |
166 | for (int i = 0; i < mZoomHoverAdapter.getCount(); i++) {
167 | View childView = mZoomHoverAdapter.getView(this, i, mZoomHoverAdapter.getItem(i));
168 | //添加view到mViewList
169 | mViewList.add(childView);
170 | childView.setId(i + 1);
171 | if (mSpanMap.containsKey(i)) {
172 | int tempRowSpan = mSpanMap.get(i).getRowSpan();
173 | int tempColumnSpan = mSpanMap.get(i).getColumnSpan();
174 | tempRowSpan = tempRowSpan >= 1 ? tempRowSpan : 1;
175 | tempColumnSpan = tempColumnSpan >= 1 ? tempColumnSpan : 1;
176 | if (tempColumnSpan > mColumnNum) {
177 | tempColumnSpan = mColumnNum;
178 | }
179 | //添加item到mItemList
180 | mItemList.add(new ZoomHoverItem(tempRowSpan, tempColumnSpan));
181 | } else {
182 | //添加item到mItemList
183 | mItemList.add(new ZoomHoverItem(1, 1));
184 | }
185 | }
186 | }
187 |
188 | /**
189 | * 计算每个item的下标
190 | */
191 | private void calculatePosition() {
192 | //计算最大的行数和列数
193 | calculateMaxRowColumn();
194 | setColumnCount(mColumnNum);
195 | setRowCount(mRowNum);
196 | //初始化数组
197 | mMatrix = new boolean[mRowNum][mColumnNum];
198 | //循环确定每个item的位置
199 | for (ZoomHoverItem item : mItemList) {
200 | setPosition(item);
201 | }
202 | }
203 |
204 | /**
205 | * 测量最差的情况下行数
206 | */
207 | private void calculateMaxRowColumn() {
208 | for (ZoomHoverItem item : mItemList) {
209 | mRowNum += item.getRowSpan();
210 | }
211 | }
212 |
213 | /**
214 | * 设置每个item的位置
215 | *
216 | * @param item
217 | */
218 | private void setPosition(ZoomHoverItem item) {
219 | //遍历每行
220 | for (int i = mStartRow; i < mRowNum; i++) {
221 | //换行以后重置起始列
222 | mStartColumn = 0;
223 | //遍历每列
224 | for (int j = mStartColumn; j < mColumnNum; j++) {
225 | if (mRowNum < i + item.getRowSpan() || mColumnNum < j + item.getColumnSpan()) {
226 | continue;
227 | }
228 | //判断是否可以放在该区域
229 | if (!canPutPosition(item, i, j)) {
230 | continue;
231 | }
232 | item.setRow(i);
233 | item.setColumn(j);
234 | mStartColumn = j;
235 | mStartRow = i;
236 | //标记已使用的位置
237 | markPosition(item, i, j);
238 | return;
239 | }
240 | }
241 | }
242 |
243 | /**
244 | * 确定是否可以放在此位置
245 | *
246 | * @param item
247 | * @param row
248 | * @param column
249 | * @return
250 | */
251 | private boolean canPutPosition(ZoomHoverItem item, int row, int column) {
252 | //从当前位置到span的区域判断
253 | for (int i = row; i < row + item.getRowSpan(); i++) {
254 | for (int j = column; j < column + item.getColumnSpan(); j++) {
255 | if (mMatrix[i][j]) {
256 | return false;
257 | }
258 | }
259 | }
260 | return true;
261 | }
262 |
263 | /**
264 | * 标记使用过的位置
265 | *
266 | * @param item
267 | * @param row
268 | * @param column
269 | */
270 | private void markPosition(ZoomHoverItem item, int row, int column) {
271 | //从当前位置到span的区域判断
272 | for (int i = row; i < row + item.getRowSpan(); i++) {
273 | for (int j = column; j < column + item.getColumnSpan(); j++) {
274 | mMatrix[i][j] = true;
275 | }
276 | }
277 | }
278 |
279 | /**
280 | * 计算布局精确的行数和列数
281 | */
282 | private void calculatePositionExact() {
283 | mColumnNum = 1;
284 | mRowNum = 1;
285 | //由行数为key,value为每行的列数
286 | HashMap rowMap = new HashMap<>();
287 | //由列数为key,value为每列的行数
288 | HashMap columnMap = new HashMap<>();
289 |
290 | for (ZoomHoverItem item : mItemList) {
291 | int row = item.getRow();
292 | int column = item.getColumn();
293 |
294 | if (rowMap.containsKey(row)) {
295 | //如果为同一行则列数相加
296 | rowMap.put(row, rowMap.get(row) + item.getColumnSpan());
297 | } else {
298 | rowMap.put(row, item.getColumnSpan());
299 | }
300 |
301 | if (columnMap.containsKey(column)) {
302 | //如果同一列,则行数相加
303 | columnMap.put(column, columnMap.get(column) + item.getRowSpan());
304 | } else {
305 | columnMap.put(column, item.getRowSpan());
306 | }
307 | }
308 |
309 | Set rowKeySet = rowMap.keySet();
310 | //遍历取出最大列数
311 | for (Integer i : rowKeySet) {
312 | int rows = rowMap.get(i);
313 | if (rows < mColumnNum) {
314 | continue;
315 | }
316 | mColumnNum = rows;
317 | }
318 |
319 | Set columnKeySet = columnMap.keySet();
320 | //遍历取出最大行数
321 | for (Integer i : columnKeySet) {
322 | int columns = columnMap.get(i);
323 | if (columns < mRowNum) {
324 | continue;
325 | }
326 | mRowNum = columns;
327 | }
328 |
329 | }
330 |
331 | /**
332 | * 将view添加进GridLayout
333 | */
334 | private void addItem() {
335 | for (int i = 0; i < mItemList.size(); i++) {
336 | View childView = mViewList.get(i);
337 | ZoomHoverItem item = mItemList.get(i);
338 | GridLayout.LayoutParams childViewLp = (LayoutParams) childView.getLayoutParams();
339 | if (childViewLp == null) {
340 | childViewLp = new GridLayout.LayoutParams();
341 | }
342 | //设置子view的宽高
343 | if (mUseBaseWH) {
344 | childViewLp.width = mBaseWidth * item.getColumnSpan() + (item.getColumnSpan() > 1 ? (item.getColumnSpan() - 1) * mDivider : 0);
345 | childViewLp.height = mBaseHeight * item.getRowSpan() + (item.getRowSpan() > 1 ? (item.getRowSpan() - 1) * mDivider : 0);
346 | } else {
347 | if (childViewLp.width > 0) {
348 | childViewLp.width = childViewLp.width * item.getColumnSpan() + (item.getColumnSpan() > 1 ? (item.getColumnSpan() - 1) * mDivider : 0);
349 | } else {
350 | childViewLp.width = LayoutParams.WRAP_CONTENT;
351 | }
352 |
353 | if (childViewLp.height > 0) {
354 | childViewLp.height = childViewLp.height * item.getRowSpan() + (item.getRowSpan() > 1 ? (item.getRowSpan() - 1) * mDivider : 0);
355 | } else {
356 | childViewLp.height = LayoutParams.WRAP_CONTENT;
357 | }
358 | }
359 | //确定子view的行列位置
360 | childViewLp.rowSpec = GridLayout.spec(item.getRow(), item.getRowSpan());
361 | childViewLp.columnSpec = GridLayout.spec(item.getColumn(), item.getColumnSpan());
362 | int right = 0;
363 | int top = 0;
364 | int bottom = 0;
365 | int left = 0;
366 | if (item.getColumn() > 0) {
367 | //除了第一列左边距为分割线的宽度
368 | left = mDivider;
369 | } else if (item.getColumn() == 0) {
370 | //第一列的左边距=距离父布局的边距
371 | left = mMarginParent;
372 | }
373 |
374 | if (item.getRow() > 0) {
375 | //不是第一行上边距为分割线的宽度
376 | top = mDivider;
377 | } else if (item.getRow() == 0) {
378 | //第一行的上边距=距离父布局的边距
379 | top = mMarginParent;
380 | }
381 |
382 | //最后一列的右边距=距离父布局的边距
383 | if (item.getColumn() + item.getColumnSpan() >= mColumnNum) {
384 | right = mMarginParent;
385 | }
386 |
387 | //最后一行的下边距=距离父布局的边距
388 | if (item.getRow() + item.getRowSpan() >= mRowNum) {
389 | bottom = mMarginParent;
390 | }
391 | //设置margin
392 | childViewLp.setMargins(left, top, right, bottom);
393 | addView(childView, childViewLp);
394 | childView.setOnClickListener(this);
395 | }
396 | }
397 |
398 |
399 | @Override
400 | public void onChanged() {
401 | changeAdapter();
402 | }
403 |
404 |
405 | @Override
406 | public void onClick(View view) {
407 | if (mCurrentView == null) {
408 | //如果currentView为null,证明第一次点击
409 | zoomInAnim(view);
410 | mCurrentView = view;
411 | if (mOnItemSelectedListener != null) {
412 | mOnItemSelectedListener.onItemSelected(mCurrentView, mCurrentView.getId() - 1);
413 | }
414 | } else {
415 | if (view.getId() != mCurrentView.getId()) {
416 | //点击的view不是currentView
417 | //currentView执行缩小动画
418 | zoomOutAnim(mCurrentView);
419 | //当前点击的view赋值给currentView
420 | mCurrentView = view;
421 | //执行放大动画
422 | zoomInAnim(mCurrentView);
423 | if (mOnItemSelectedListener != null) {
424 | mOnItemSelectedListener.onItemSelected(mCurrentView, mCurrentView.getId() - 1);
425 | }
426 | }
427 | }
428 | }
429 |
430 | /**
431 | * 放大动画
432 | *
433 | * @param view
434 | */
435 | private void zoomInAnim(final View view) {
436 | //将view放在其他view之上
437 | view.bringToFront();
438 | //按照bringToFront文档来的,暂没测试
439 | if (Build.VERSION.SDK_INT < KITKAT) {
440 | requestLayout();
441 | }
442 | if (mCurrentZoomInAnim != null) {
443 | //如果当前有放大动画执行,cancel调
444 | mCurrentZoomInAnim.cancel();
445 | }
446 | ObjectAnimator objectAnimatorX = ObjectAnimator.ofFloat(view, "scaleX", 1.0f, mAnimZoomTo);
447 | ObjectAnimator objectAnimatorY = ObjectAnimator.ofFloat(view, "scaleY", 1.0f, mAnimZoomTo);
448 | objectAnimatorX.setDuration(mAnimDuration);
449 | objectAnimatorX.setInterpolator(mZoomInInterpolator);
450 | objectAnimatorY.setDuration(mAnimDuration);
451 | objectAnimatorY.setInterpolator(mZoomInInterpolator);
452 | AnimatorSet set = new AnimatorSet();
453 | set.playTogether(objectAnimatorX, objectAnimatorY);
454 |
455 | set.addListener(new Animator.AnimatorListener() {
456 | @Override
457 | public void onAnimationStart(Animator animator) {
458 | //放大动画开始
459 | if (mOnZoomAnimatorListener != null) {
460 | mOnZoomAnimatorListener.onZoomInStart(view);
461 | }
462 | }
463 |
464 | @Override
465 | public void onAnimationEnd(Animator animator) {
466 | //放大动画结束
467 | if (mOnZoomAnimatorListener != null) {
468 | mOnZoomAnimatorListener.onZoomInEnd(view);
469 | }
470 | mCurrentZoomInAnim = null;
471 | }
472 |
473 | @Override
474 | public void onAnimationCancel(Animator animator) {
475 | //放大动画退出
476 | mCurrentZoomInAnim = null;
477 | }
478 |
479 | @Override
480 | public void onAnimationRepeat(Animator animator) {
481 |
482 | }
483 | });
484 | set.start();
485 | mCurrentZoomInAnim = set;
486 | }
487 |
488 | /**
489 | * 缩小动画
490 | *
491 | * @param view
492 | */
493 | private void zoomOutAnim(final View view) {
494 | if (mCurrentZoomOutAnim != null) {
495 | //如果当前有缩小动画执行,cancel调
496 | mCurrentZoomOutAnim.cancel();
497 | //动画cancel后,上一个缩小view的scaleX不是1.0,就手动设置scaleX,Y到1.0
498 | if (mPreZoomOutView != null && mPreZoomOutView.getScaleX() > 1.0) {
499 | mPreZoomOutView.setScaleX(1.0f);
500 | mPreZoomOutView.setScaleY(1.0f);
501 | }
502 | }
503 | ObjectAnimator objectAnimatorX = ObjectAnimator.ofFloat(view, "scaleX", mAnimZoomTo, 1.0f);
504 | ObjectAnimator objectAnimatorY = ObjectAnimator.ofFloat(view, "scaleY", mAnimZoomTo, 1.0f);
505 | objectAnimatorX.setDuration(mAnimDuration);
506 | objectAnimatorX.setInterpolator(mZoomOutInterpolator);
507 | objectAnimatorY.setDuration(mAnimDuration);
508 | objectAnimatorY.setInterpolator(mZoomOutInterpolator);
509 | AnimatorSet set = new AnimatorSet();
510 | set.playTogether(objectAnimatorX, objectAnimatorY);
511 |
512 | set.addListener(new Animator.AnimatorListener() {
513 | @Override
514 | public void onAnimationStart(Animator animator) {
515 | //缩小动画开始
516 | if (mOnZoomAnimatorListener != null) {
517 | mOnZoomAnimatorListener.onZoomOutStart(view);
518 | }
519 | }
520 |
521 | @Override
522 | public void onAnimationEnd(Animator animator) {
523 | //缩小动画结束
524 | if (mOnZoomAnimatorListener != null) {
525 | mOnZoomAnimatorListener.onZoomOutEnd(view);
526 | }
527 | mCurrentZoomOutAnim = null;
528 | }
529 |
530 | @Override
531 | public void onAnimationCancel(Animator animator) {
532 | //缩小动画退出
533 | mCurrentZoomOutAnim = null;
534 | }
535 |
536 | @Override
537 | public void onAnimationRepeat(Animator animator) {
538 |
539 | }
540 | });
541 | set.start();
542 | mCurrentZoomOutAnim = set;
543 | //把当前缩放的view作为上一个缩放的view
544 | mPreZoomOutView = view;
545 | }
546 |
547 | /**
548 | * 设置缩放动画监听器
549 | *
550 | * @param listener
551 | */
552 | public void setOnZoomAnimatorListener(OnZoomAnimatorListener listener) {
553 | this.mOnZoomAnimatorListener = listener;
554 | }
555 |
556 | /**
557 | * 同时设置插值器
558 | *
559 | * @param interpolator
560 | */
561 | public void setZoomInterpolator(Interpolator interpolator) {
562 | this.mZoomInInterpolator = this.mZoomOutInterpolator = interpolator;
563 | }
564 |
565 | /**
566 | * 设置放大动画插值器
567 | *
568 | * @param interpolator
569 | */
570 | public void setZoomInInterpolator(Interpolator interpolator) {
571 | this.mZoomInInterpolator = interpolator;
572 | }
573 |
574 | /**
575 | * 设置缩小动画的插值器
576 | *
577 | * @param interpolator
578 | */
579 | public void setZoomOutInterpolator(Interpolator interpolator) {
580 | this.mZoomOutInterpolator = interpolator;
581 | }
582 |
583 | /**
584 | * 设置item选中监听器
585 | *
586 | * @param listener
587 | */
588 | public void setOnItemSelectedListener(OnItemSelectedListener listener) {
589 | this.mOnItemSelectedListener = listener;
590 | }
591 |
592 | /**
593 | * 设置选中的条目
594 | *
595 | * @param position
596 | */
597 | public void setSelectedItem(int position) {
598 | //list为空或者size map) {
673 | this.mSpanMap.clear();
674 | this.mSpanMap.putAll(map);
675 | if (mZoomHoverAdapter != null) {
676 | //如果adapter不为null重新布局
677 | changeAdapter();
678 | }
679 | }
680 |
681 | /**
682 | * 添加跨度item
683 | *
684 | * @param item
685 | */
686 | public void addSpanItem(int position, ZoomHoverSpan item) {
687 | this.mSpanMap.put(position, item);
688 | if (mZoomHoverAdapter != null) {
689 | //如果adapter不为null重新布局
690 | changeAdapter();
691 | }
692 | }
693 |
694 | /**
695 | * 添加跨度信息
696 | *
697 | * @param position
698 | * @param rowSpan
699 | * @param columnSpan
700 | */
701 | public void addSpanItem(int position, int rowSpan, int columnSpan) {
702 | this.mSpanMap.put(position, new ZoomHoverSpan(rowSpan, columnSpan));
703 | if (mZoomHoverAdapter != null) {
704 | //如果adapter不为null重新布局
705 | changeAdapter();
706 | }
707 | }
708 |
709 | /**
710 | * 设置是否使用基础宽高
711 | *
712 | * @param useBaseWH
713 | */
714 | public void setUseBaseWH(boolean useBaseWH) {
715 | this.mUseBaseWH = useBaseWH;
716 | }
717 |
718 | /**
719 | * 设置基础的宽度
720 | *
721 | * @param baseWidth
722 | */
723 | public void setBaseWidth(int baseWidth) {
724 | this.mBaseWidth = baseWidth;
725 | }
726 |
727 | /**
728 | * 设置基础高度
729 | *
730 | * @param baseHeight
731 | */
732 | public void setBaseHeight(int baseHeight) {
733 | this.mBaseHeight = baseHeight;
734 | }
735 |
736 | }
737 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zyyoona7/customviewsets/zoom_hover/ZoomHoverItem.java:
--------------------------------------------------------------------------------
1 | package com.zyyoona7.customviewsets.zoom_hover;
2 |
3 | import android.os.Parcel;
4 | import android.os.Parcelable;
5 |
6 | /**
7 | * Created by zyyoona7 on 2016/8/25.
8 | * 设置span时候需要
9 | */
10 |
11 | public class ZoomHoverItem implements Parcelable {
12 |
13 | //---row,column确定坐标
14 | //行
15 | private int row;
16 |
17 | //列
18 | private int column;
19 |
20 | //---支持跨行,跨列
21 | //横跨行
22 | private int rowSpan=1;
23 |
24 | //横跨列
25 | private int columnSpan=1;
26 |
27 | public ZoomHoverItem(){
28 |
29 | }
30 |
31 | public ZoomHoverItem(int rowSpan, int columnSpan) {
32 | this.rowSpan = rowSpan;
33 | this.columnSpan = columnSpan;
34 | }
35 |
36 | public ZoomHoverItem(int row, int column, int rowSpan, int columnSpan) {
37 | this.row = row;
38 | this.column = column;
39 | this.rowSpan = rowSpan;
40 | this.columnSpan = columnSpan;
41 | }
42 |
43 | protected ZoomHoverItem(Parcel in) {
44 | row = in.readInt();
45 | column = in.readInt();
46 | rowSpan = in.readInt();
47 | columnSpan = in.readInt();
48 | }
49 |
50 | public static final Creator CREATOR = new Creator() {
51 | @Override
52 | public ZoomHoverItem createFromParcel(Parcel in) {
53 | return new ZoomHoverItem(in);
54 | }
55 |
56 | @Override
57 | public ZoomHoverItem[] newArray(int size) {
58 | return new ZoomHoverItem[size];
59 | }
60 | };
61 |
62 | public int getColumnSpan() {
63 | return columnSpan;
64 | }
65 |
66 | public void setColumnSpan(int columnSpan) {
67 | this.columnSpan = columnSpan;
68 | }
69 |
70 | public int getRow() {
71 | return row;
72 | }
73 |
74 | public void setRow(int row) {
75 | this.row = row;
76 | }
77 |
78 | public int getColumn() {
79 | return column;
80 | }
81 |
82 | public void setColumn(int column) {
83 | this.column = column;
84 | }
85 |
86 | public int getRowSpan() {
87 | return rowSpan;
88 | }
89 |
90 | public void setRowSpan(int rowSpan) {
91 | this.rowSpan = rowSpan;
92 | }
93 |
94 | @Override
95 | public int describeContents() {
96 | return 0;
97 | }
98 |
99 | @Override
100 | public void writeToParcel(Parcel dest, int flags) {
101 | dest.writeInt(row);
102 | dest.writeInt(column);
103 | dest.writeInt(rowSpan);
104 | dest.writeInt(columnSpan);
105 | }
106 |
107 | @Override
108 | public boolean equals(Object o) {
109 | ZoomHoverItem item= (ZoomHoverItem) o;
110 | return item.getRow()==row && item.getColumn()==column;
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zyyoona7/customviewsets/zoom_hover/ZoomHoverSpan.java:
--------------------------------------------------------------------------------
1 | package com.zyyoona7.customviewsets.zoom_hover;
2 |
3 | /**
4 | * Created by zyyoona7 on 2016/8/25.
5 | *
6 | * 设置对应的rowSpan,columnSpan
7 | */
8 |
9 | public class ZoomHoverSpan {
10 |
11 | private int rowSpan;
12 |
13 | private int columnSpan;
14 |
15 | public ZoomHoverSpan() {
16 | }
17 |
18 | public ZoomHoverSpan(int rowSpan, int columnSpan) {
19 | this.rowSpan = rowSpan;
20 | this.columnSpan = columnSpan;
21 | }
22 |
23 | public int getRowSpan() {
24 | return rowSpan;
25 | }
26 |
27 | public void setRowSpan(int rowSpan) {
28 | this.rowSpan = rowSpan;
29 | }
30 |
31 | public int getColumnSpan() {
32 | return columnSpan;
33 | }
34 |
35 | public void setColumnSpan(int columnSpan) {
36 | this.columnSpan = columnSpan;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zyyoona7/customviewsets/zoom_hover/ZoomHoverView.java:
--------------------------------------------------------------------------------
1 | package com.zyyoona7.customviewsets.zoom_hover;
2 |
3 | import android.animation.Animator;
4 | import android.animation.AnimatorSet;
5 | import android.animation.ObjectAnimator;
6 | import android.content.Context;
7 | import android.content.res.TypedArray;
8 | import android.graphics.Canvas;
9 | import android.os.Build;
10 | import android.support.v4.util.SimpleArrayMap;
11 | import android.util.AttributeSet;
12 | import android.util.Log;
13 | import android.view.View;
14 | import android.view.ViewGroup;
15 | import android.view.animation.AccelerateDecelerateInterpolator;
16 | import android.view.animation.Interpolator;
17 | import android.widget.RelativeLayout;
18 |
19 | import com.zyyoona7.customviewsets.R;
20 | import com.zyyoona7.customviewsets.utils.DensityUtils;
21 |
22 | import java.util.ArrayList;
23 | import java.util.List;
24 |
25 | import static android.os.Build.VERSION_CODES.KITKAT;
26 |
27 | /**
28 | * Created by zyyoona7 on 2016/8/16.
29 | */
30 |
31 | public class ZoomHoverView extends RelativeLayout implements View.OnClickListener, ZoomHoverAdapter.OnDataChangedListener {
32 |
33 | private static final String TAG = "ZoomHoverView";
34 |
35 | //adapter
36 | private ZoomHoverAdapter mZoomHoverAdapter;
37 |
38 | // 需要的列数
39 | private int mColumnNum = 3;
40 |
41 | //记录当前列
42 | private int mCurrentColumn = 0;
43 |
44 | //记录当前行
45 | private int mCurrentRow = 1;
46 |
47 | //记录每行第一列的下标(row First column position)
48 | //K--所在行数 V--当前view的下标
49 | private SimpleArrayMap mRFColPosMap = new SimpleArrayMap<>();
50 |
51 | //子view距离父控件的外边距宽度
52 | private int mMarginParent = 20;
53 |
54 | //行列的分割线宽度
55 | private int mDivider = 10;
56 |
57 | //当前放大动画
58 | private AnimatorSet mCurrentZoomInAnim = null;
59 |
60 | //当前缩小动画
61 | private AnimatorSet mCurrentZoomOutAnim = null;
62 |
63 | //缩放动画监听器
64 | private OnZoomAnimatorListener mOnZoomAnimatorListener = null;
65 |
66 | //动画持续时间
67 | private int mAnimDuration;
68 |
69 | //动画缩放倍数
70 | private float mAnimZoomTo;
71 |
72 | //缩放动画插值器
73 | private Interpolator mZoomInInterpolator;
74 | private Interpolator mZoomOutInterpolator;
75 |
76 | //上一个ZoomOut的view(为了解决快速切换时,上一个被缩小的view缩放大小不正常的情况)
77 | private View mPreZoomOutView;
78 |
79 | //当前被选中的view
80 | private View mCurrentView = null;
81 |
82 | //item选中监听器
83 | private OnItemSelectedListener mOnItemSelectedListener;
84 |
85 | //存储当前layout中所有子view
86 | private List mViewList;
87 |
88 | //需要横跨的map
89 | private SimpleArrayMap mNeedSpanMap = new SimpleArrayMap<>();
90 |
91 | public ZoomHoverView(Context context) {
92 | this(context, null);
93 | }
94 |
95 | public ZoomHoverView(Context context, AttributeSet attrs) {
96 | super(context, attrs);
97 | TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ZoomHoverView);
98 | mDivider = typedArray.getDimensionPixelOffset(R.styleable.ZoomHoverView_zhv_divider, DensityUtils.dip2px(context, 5));
99 | mMarginParent = typedArray.getDimensionPixelOffset(R.styleable.ZoomHoverView_zhv_margin_parent, DensityUtils.dip2px(context, 5));
100 | mColumnNum = typedArray.getInt(R.styleable.ZoomHoverView_zhv_column_num, 3);
101 | mAnimDuration = typedArray.getInt(R.styleable.ZoomHoverView_zhv_zoom_duration, getResources().getInteger(android.R.integer.config_shortAnimTime));
102 | mAnimZoomTo = typedArray.getFloat(R.styleable.ZoomHoverView_zhv_zoom_to, 1.2f);
103 | typedArray.recycle();
104 | mZoomOutInterpolator = mZoomInInterpolator = new AccelerateDecelerateInterpolator();
105 | }
106 |
107 | @Override
108 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
109 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
110 |
111 | Log.e(TAG, "onMeasure: 执行了..." + getWidth() + "," + getHeight());
112 | }
113 |
114 | @Override
115 | protected void onDraw(Canvas canvas) {
116 | super.onDraw(canvas);
117 | Log.e(TAG, "onDraw: 执行了...");
118 | }
119 |
120 | /**
121 | * 设置适配器
122 | *
123 | * @param adapter
124 | */
125 |
126 | public void setAdapter(ZoomHoverAdapter adapter) {
127 | this.mZoomHoverAdapter = adapter;
128 | mZoomHoverAdapter.setDataChangedListener(this);
129 | changeAdapter();
130 | }
131 |
132 |
133 | /**
134 | * 根据adapter添加view
135 | */
136 | private void changeAdapter() {
137 | removeAllViews();
138 | //重置参数(因为changeAdapter可能调用多次)
139 | mColumnNum = 3;
140 | mCurrentRow = 1;
141 | mCurrentColumn = 0;
142 | mRFColPosMap.clear();
143 | requestLayout();
144 | mViewList = new ArrayList<>(mZoomHoverAdapter.getCount());
145 | //需要拉伸的下标的参数K-下标,V-跨度
146 | SimpleArrayMap needSpanMap = mNeedSpanMap;
147 | for (int i = 0; i < mZoomHoverAdapter.getCount(); i++) {
148 | //获取子view
149 | View childView = mZoomHoverAdapter.getView(this, i, mZoomHoverAdapter.getItem(i));
150 | mViewList.add(childView);
151 | childView.setId(i + 1);
152 |
153 | //判断当前view是否设置了跨度
154 | int span = 1;
155 | if (needSpanMap.containsKey(i)) {
156 | span = needSpanMap.get(i);
157 | }
158 |
159 | //获取AdapterView的的布局参数
160 | RelativeLayout.LayoutParams childViewParams = (LayoutParams) childView.getLayoutParams();
161 |
162 | if (childViewParams == null) {
163 | childViewParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
164 | }
165 |
166 | //如果view的宽高设置了wrap_content或者match_parent则span无效
167 | if (childViewParams.width <= 0) {
168 | span = 1;
169 | }
170 |
171 | //如果跨度有变,重新设置view的宽
172 | if (span > 1 && span <= mColumnNum) {
173 | childViewParams.width = childViewParams.width * span + (span - 1) * mDivider;
174 | } else if (span < 1) {
175 | span = 1;
176 | } else if (span > mColumnNum) {
177 | span = mColumnNum;
178 | childViewParams.width = childViewParams.width * span + (span - 1) * mDivider;
179 | }
180 |
181 | //设置右下左上的边距
182 | int rightMargin = 0;
183 | int bottomMargin = 0;
184 | int leftMargin = 0;
185 | int topMargin = 0;
186 |
187 | //如果跨度+当前的列>设置的列数,换行
188 | if (span + mCurrentColumn > mColumnNum) {
189 | //换行当前行数+1
190 | mCurrentRow++;
191 | //当前列等于当前view的跨度
192 | mCurrentColumn = span;
193 | //换行以后肯定是第一个
194 | mRFColPosMap.put(mCurrentRow, i);
195 | //换行操作
196 | //因为换行,肯定不是第一行
197 | //换行操作后将当前view添加到上一行第一个位置的下面
198 | childViewParams.addRule(RelativeLayout.BELOW,
199 | mViewList.get(mRFColPosMap.get(mCurrentRow - 1)).getId());
200 | //不是第一行,所以上边距为分割线的宽度
201 | topMargin = mDivider;
202 | //换行后位置在左边第一个,所以左边距为距离父控件的边距
203 | leftMargin = mMarginParent;
204 | } else {
205 | if (mCurrentColumn <= 0 && mCurrentRow <= 1) {
206 | //第一行第一列的位置保存第一列信息,同时第一列不需要任何相对规则
207 | mRFColPosMap.put(mCurrentRow, i);
208 | //第一行第一列上边距和左边距都是距离父控件的边距
209 | topMargin = mMarginParent;
210 | leftMargin = mMarginParent;
211 | } else {
212 | //不是每一行的第一个,就添加到前一个的view的右面,并且和前一个顶部对齐
213 | childViewParams.addRule(RelativeLayout.RIGHT_OF,
214 | mViewList.get(i - 1).getId());
215 | childViewParams.addRule(ALIGN_TOP, mViewList.get(i - 1).getId());
216 |
217 | }
218 | //移动到当前列
219 | mCurrentColumn += span;
220 | }
221 |
222 | if (mCurrentColumn >= mColumnNum || i >= mZoomHoverAdapter.getCount() - 1) {
223 | //如果当前列为列总数或者当前view的下标等于最后一个view的下标那么就是最右边的view,设置父边距
224 | rightMargin = mMarginParent;
225 | } else {
226 | rightMargin = mDivider;
227 | }
228 |
229 | //如果当前view是最后一个那么他肯定是最后一行
230 | if (i >= (mZoomHoverAdapter.getCount() - 1)) {
231 | bottomMargin = mMarginParent;
232 | }
233 |
234 | //设置外边距
235 | childViewParams.setMargins(leftMargin, topMargin, rightMargin,
236 | bottomMargin);
237 | //添加view
238 | addView(childView, childViewParams);
239 | //添加点击事件
240 | childView.setOnClickListener(this);
241 | }
242 | }
243 |
244 |
245 | @Override
246 | public void onChanged() {
247 | changeAdapter();
248 | }
249 |
250 |
251 | @Override
252 | public void onClick(View view) {
253 | if (mCurrentView == null) {
254 | //如果currentView为null,证明第一次点击
255 | zoomInAnim(view);
256 | mCurrentView = view;
257 | if (mOnItemSelectedListener != null) {
258 | mOnItemSelectedListener.onItemSelected(mCurrentView, mCurrentView.getId() - 1);
259 | }
260 | } else {
261 | if (view.getId() != mCurrentView.getId()) {
262 | //点击的view不是currentView
263 | //currentView执行缩小动画
264 | zoomOutAnim(mCurrentView);
265 | //当前点击的view赋值给currentView
266 | mCurrentView = view;
267 | //执行放大动画
268 | zoomInAnim(mCurrentView);
269 | if (mOnItemSelectedListener != null) {
270 | mOnItemSelectedListener.onItemSelected(mCurrentView, mCurrentView.getId() - 1);
271 | }
272 | }
273 | }
274 | }
275 |
276 | /**
277 | * 放大动画
278 | *
279 | * @param view
280 | */
281 | private void zoomInAnim(final View view) {
282 | //将view放在其他view之上
283 | view.bringToFront();
284 | //按照bringToFront文档来的,暂没测试
285 | if (Build.VERSION.SDK_INT < KITKAT) {
286 | requestLayout();
287 | }
288 | if (mCurrentZoomInAnim != null) {
289 | //如果当前有放大动画执行,cancel调
290 | mCurrentZoomInAnim.cancel();
291 | }
292 | ObjectAnimator objectAnimatorX = ObjectAnimator.ofFloat(view, "scaleX", 1.0f, mAnimZoomTo);
293 | ObjectAnimator objectAnimatorY = ObjectAnimator.ofFloat(view, "scaleY", 1.0f, mAnimZoomTo);
294 | objectAnimatorX.setDuration(mAnimDuration);
295 | objectAnimatorX.setInterpolator(mZoomInInterpolator);
296 | objectAnimatorY.setDuration(mAnimDuration);
297 | objectAnimatorY.setInterpolator(mZoomInInterpolator);
298 | AnimatorSet set = new AnimatorSet();
299 | set.playTogether(objectAnimatorX, objectAnimatorY);
300 |
301 | set.addListener(new Animator.AnimatorListener() {
302 | @Override
303 | public void onAnimationStart(Animator animator) {
304 | //放大动画开始
305 | if (mOnZoomAnimatorListener != null) {
306 | mOnZoomAnimatorListener.onZoomInStart(view);
307 | }
308 | }
309 |
310 | @Override
311 | public void onAnimationEnd(Animator animator) {
312 | //放大动画结束
313 | if (mOnZoomAnimatorListener != null) {
314 | mOnZoomAnimatorListener.onZoomInEnd(view);
315 | }
316 | mCurrentZoomInAnim = null;
317 | }
318 |
319 | @Override
320 | public void onAnimationCancel(Animator animator) {
321 | //放大动画退出
322 | mCurrentZoomInAnim = null;
323 | }
324 |
325 | @Override
326 | public void onAnimationRepeat(Animator animator) {
327 |
328 | }
329 | });
330 | set.start();
331 | mCurrentZoomInAnim = set;
332 | }
333 |
334 | /**
335 | * 缩小动画
336 | *
337 | * @param view
338 | */
339 | private void zoomOutAnim(final View view) {
340 | if (mCurrentZoomOutAnim != null) {
341 | //如果当前有缩小动画执行,cancel调
342 | mCurrentZoomOutAnim.cancel();
343 | //动画cancel后,上一个缩小view的scaleX不是1.0,就手动设置scaleX,Y到1.0
344 | if (mPreZoomOutView != null && mPreZoomOutView.getScaleX() > 1.0) {
345 | mPreZoomOutView.setScaleX(1.0f);
346 | mPreZoomOutView.setScaleY(1.0f);
347 | }
348 | }
349 | ObjectAnimator objectAnimatorX = ObjectAnimator.ofFloat(view, "scaleX", mAnimZoomTo, 1.0f);
350 | ObjectAnimator objectAnimatorY = ObjectAnimator.ofFloat(view, "scaleY", mAnimZoomTo, 1.0f);
351 | objectAnimatorX.setDuration(mAnimDuration);
352 | objectAnimatorX.setInterpolator(mZoomOutInterpolator);
353 | objectAnimatorY.setDuration(mAnimDuration);
354 | objectAnimatorY.setInterpolator(mZoomOutInterpolator);
355 | AnimatorSet set = new AnimatorSet();
356 | set.playTogether(objectAnimatorX, objectAnimatorY);
357 |
358 | set.addListener(new Animator.AnimatorListener() {
359 | @Override
360 | public void onAnimationStart(Animator animator) {
361 | //缩小动画开始
362 | if (mOnZoomAnimatorListener != null) {
363 | mOnZoomAnimatorListener.onZoomOutStart(view);
364 | }
365 | }
366 |
367 | @Override
368 | public void onAnimationEnd(Animator animator) {
369 | //缩小动画结束
370 | if (mOnZoomAnimatorListener != null) {
371 | mOnZoomAnimatorListener.onZoomOutEnd(view);
372 | }
373 | mCurrentZoomOutAnim = null;
374 | }
375 |
376 | @Override
377 | public void onAnimationCancel(Animator animator) {
378 | //缩小动画退出
379 | mCurrentZoomOutAnim = null;
380 | }
381 |
382 | @Override
383 | public void onAnimationRepeat(Animator animator) {
384 |
385 | }
386 | });
387 | set.start();
388 | mCurrentZoomOutAnim = set;
389 | //把当前缩放的view作为上一个缩放的view
390 | mPreZoomOutView = view;
391 | }
392 |
393 | /**
394 | * 设置缩放动画监听器
395 | *
396 | * @param listener
397 | */
398 | public void setOnZoomAnimatorListener(OnZoomAnimatorListener listener) {
399 | this.mOnZoomAnimatorListener = listener;
400 | }
401 |
402 | /**
403 | * 同时设置插值器
404 | *
405 | * @param interpolator
406 | */
407 | public void setZoomInterpolator(Interpolator interpolator) {
408 | this.mZoomInInterpolator = this.mZoomOutInterpolator = interpolator;
409 | }
410 |
411 | /**
412 | * 设置放大动画插值器
413 | *
414 | * @param interpolator
415 | */
416 | public void setZoomInInterpolator(Interpolator interpolator) {
417 | this.mZoomInInterpolator = interpolator;
418 | }
419 |
420 | /**
421 | * 设置缩小动画的插值器
422 | *
423 | * @param interpolator
424 | */
425 | public void setZoomOutInterpolator(Interpolator interpolator) {
426 | this.mZoomOutInterpolator = interpolator;
427 | }
428 |
429 | /**
430 | * 设置item选中监听器
431 | *
432 | * @param listener
433 | */
434 | public void setOnItemSelectedListener(OnItemSelectedListener listener) {
435 | this.mOnItemSelectedListener = listener;
436 | }
437 |
438 | /**
439 | * 设置选中的条目
440 | *
441 | * @param position
442 | */
443 | public void setSelectedItem(int position) {
444 | //list为空或者size map) {
520 | this.mNeedSpanMap.clear();
521 | this.mNeedSpanMap.putAll(map);
522 | if (this.mZoomHoverAdapter != null) {
523 | changeAdapter();
524 | }
525 | }
526 |
527 | public void addSpan(int position, int span) {
528 | this.mNeedSpanMap.clear();
529 | this.mNeedSpanMap.put(position, span);
530 | if (this.mZoomHoverAdapter != null) {
531 | changeAdapter();
532 | }
533 | }
534 |
535 | }
536 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/heart_default.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zyyoona7/CustomViewSets/6afe3a792ebe2b6c11601fc2b352170204ac9baf/app/src/main/res/drawable-xxhdpi/heart_default.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ss_heart1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zyyoona7/CustomViewSets/6afe3a792ebe2b6c11601fc2b352170204ac9baf/app/src/main/res/drawable-xxhdpi/ss_heart1.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ss_heart2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zyyoona7/CustomViewSets/6afe3a792ebe2b6c11601fc2b352170204ac9baf/app/src/main/res/drawable-xxhdpi/ss_heart2.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ss_heart3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zyyoona7/CustomViewSets/6afe3a792ebe2b6c11601fc2b352170204ac9baf/app/src/main/res/drawable-xxhdpi/ss_heart3.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ss_heart4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zyyoona7/CustomViewSets/6afe3a792ebe2b6c11601fc2b352170204ac9baf/app/src/main/res/drawable-xxhdpi/ss_heart4.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ss_heart5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zyyoona7/CustomViewSets/6afe3a792ebe2b6c11601fc2b352170204ac9baf/app/src/main/res/drawable-xxhdpi/ss_heart5.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/shadow_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
5 |
6 |
7 |
8 |
9 |
10 |
11 | -
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_basic_operation.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
15 |
16 |
20 |
21 |
24 |
25 |
26 |
27 |
30 |
31 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_card.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
19 |
20 |
26 |
27 |
33 |
34 |
35 |
40 |
41 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_cover_flow.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_heart.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_layout_manager.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
17 |
18 |
23 |
24 |
29 |
30 |
35 |
36 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_zoom_hover.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
18 |
19 |
23 |
24 |
28 |
29 |
36 |
37 |
41 |
42 |
55 |
56 |
57 |
58 |
59 |
60 |
64 |
65 |
72 |
73 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
15 |
16 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_overlay_card.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_pagination.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zyyoona7/CustomViewSets/6afe3a792ebe2b6c11601fc2b352170204ac9baf/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zyyoona7/CustomViewSets/6afe3a792ebe2b6c11601fc2b352170204ac9baf/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zyyoona7/CustomViewSets/6afe3a792ebe2b6c11601fc2b352170204ac9baf/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/yoona.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zyyoona7/CustomViewSets/6afe3a792ebe2b6c11601fc2b352170204ac9baf/app/src/main/res/mipmap-xhdpi/yoona.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zyyoona7/CustomViewSets/6afe3a792ebe2b6c11601fc2b352170204ac9baf/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zyyoona7/CustomViewSets/6afe3a792ebe2b6c11601fc2b352170204ac9baf/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | CustomViewSets
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/test/java/com/zyyoona7/customviewsets/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.zyyoona7.customviewsets;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | mavenCentral()
7 | }
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:2.3.0'
10 | classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
11 |
12 | // NOTE: Do not place your application dependencies here; they belong
13 | // in the individual module build.gradle files
14 | }
15 | }
16 |
17 | allprojects {
18 | repositories {
19 | jcenter()
20 | mavenCentral()
21 | maven { url "https://jitpack.io" }
22 | }
23 | }
24 |
25 | task clean(type: Delete) {
26 | delete rootProject.buildDir
27 | }
28 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zyyoona7/CustomViewSets/6afe3a792ebe2b6c11601fc2b352170204ac9baf/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sat Apr 01 14:05:18 CST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/images/basic_operation.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zyyoona7/CustomViewSets/6afe3a792ebe2b6c11601fc2b352170204ac9baf/images/basic_operation.gif
--------------------------------------------------------------------------------
/images/cardLayoutManager.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zyyoona7/CustomViewSets/6afe3a792ebe2b6c11601fc2b352170204ac9baf/images/cardLayoutManager.gif
--------------------------------------------------------------------------------
/images/heart_view.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zyyoona7/CustomViewSets/6afe3a792ebe2b6c11601fc2b352170204ac9baf/images/heart_view.gif
--------------------------------------------------------------------------------
/images/paginationRv.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zyyoona7/CustomViewSets/6afe3a792ebe2b6c11601fc2b352170204ac9baf/images/paginationRv.gif
--------------------------------------------------------------------------------
/images/zoomhover.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zyyoona7/CustomViewSets/6afe3a792ebe2b6c11601fc2b352170204ac9baf/images/zoomhover.gif
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------