├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro ├── src │ ├── androidTest │ │ └── java │ │ │ ├── chen │ │ │ └── you │ │ │ │ └── wheelviewtest │ │ │ │ └── ExampleInstrumentedTest.java │ │ │ └── you │ │ │ └── xiaochen │ │ │ └── wheel │ │ │ └── ExampleInstrumentedTest.java │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ │ └── chen │ │ │ │ └── you │ │ │ │ └── wheelviewtest │ │ │ │ ├── MainActivity.java │ │ │ │ ├── TestDatas.java │ │ │ │ └── TwoActivity.java │ │ └── res │ │ │ ├── drawable-v24 │ │ │ └── ic_launcher_foreground.xml │ │ │ ├── drawable │ │ │ └── ic_launcher_background.xml │ │ │ ├── layout │ │ │ ├── act_test.xml │ │ │ ├── activity_main.xml │ │ │ └── activity_two.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── values-w820dp │ │ │ └── dimens.xml │ │ │ └── values │ │ │ ├── attrs.xml │ │ │ ├── colors.xml │ │ │ ├── dimens.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ └── test │ │ └── java │ │ ├── chen │ │ └── you │ │ │ └── wheelviewtest │ │ │ └── ExampleUnitTest.java │ │ └── you │ │ └── xiaochen │ │ └── wheel │ │ └── ExampleUnitTest.java └── you.jks ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── imgs ├── GIF111.gif ├── GIF222.gif ├── demo.apk └── 原理图.png ├── settings.gradle ├── wheelview.apk └── wheelview ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src ├── androidTest └── java │ └── chen │ └── you │ └── wheel │ └── ExampleInstrumentedTest.java ├── main ├── AndroidManifest.xml ├── java │ └── chen │ │ └── you │ │ └── wheel │ │ ├── LinearDrawManager.java │ │ ├── WheelDrawManager.java │ │ ├── WheelParams.java │ │ └── WheelView.java └── res │ └── values │ └── wheelview_attrs.xml └── test └── java └── chen └── you └── wheel └── ExampleUnitTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | *.DS_Store 3 | /.gradle 4 | /.idea 5 | /build 6 | /gradlew 7 | /gradlew.bat 8 | /local.properties 9 | /captures 10 | .externalNativeBuild -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WheelView-3d 2 | ### WheelView extends ViewGroup, Camera, Matrix 实现WheelView 3d效果 3 | 4 | ## 使用, 1.4.1版本有大改动, 使用1.4.1之前最新的版本号为 1.3.0 5 | ``` 6 | allprojects { 7 | repositories { 8 | ... 9 | maven { url 'https://jitpack.io' } 10 | } 11 | } 12 | 13 | dependencies { 14 | implementation 'com.github.youxiaochen:WheelView-3d:1.4.1' 15 | } 16 | ``` 17 | 18 | ### 如果要实现比较复杂的任何布局方式整个childView旋转效果可以参考博客  http://www.jianshu.com/p/41e4602deca6 19 | 20 | ### 博客地址 http://www.jianshu.com/p/77656dbb07b2 21 | 22 | #### [测试包下载](imgs/demo.apk) 23 | 24 | ![纵向排列](imgs/GIF111.gif) 25 | 26 |
27 | 28 | ![水平排列](imgs/GIF222.gif) 29 |
30 | ![水平排列](imgs/原理图.png) 31 | 32 | 33 | ``` 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | ``` 67 | 68 | ### 布局生成WheelView使用方式, 不设置时使用将使用默认值 69 | 70 | ``` 71 | 86 | 87 | wv.setAdapter(new WheelView.Adapter() { 88 | @Override 89 | protected String getItem(int position) { 90 | return "position " + position; 91 | } 92 | 93 | @Override 94 | protected int getItemCount() { 95 | return 100; 96 | } 97 | }); 98 | ``` 99 | 100 | ### 代码生成WheelView及更多使用方式, WheelParams, DrawManager, ItemPainter都有默认值可不设置 101 | ``` 102 | WheelParams params = new WheelParams.Builder() 103 | .setOrientation(WheelParams.HORIZONTAL) 104 | .setItemSize(...) 105 | .setTextColor(...) 106 | WheelView wv = new WheelView(context, params); 107 | ... 108 | //代码设置各种属性 109 | //亦可用此方式设置各属性 110 | wv.getWheelParams().newBuilder().setOrientation(...) 111 | wv.setWheelParams(params); 112 | //设置绘制管理, 默认为WheelDrawManager产生3D旋转, 亦可设置LinearDrawManager不旋转, 也可自定义DrawManager扩展 113 | wv.setDrawManager(new WheelDrawManager()); 114 | //设置绘制器, 默认为SimpleItemPainter, 也可自定义绘制器扩展 115 | wv.setItemPainter(...) 116 | ``` 117 | 118 | ### E-mail-QQ: 86207610@qq.com W: 86207610 Welcome to star 119 | 120 | #### 博客地址 http://www.jianshu.com/p/77656dbb07b2 121 | 122 | #### 如果要实现比较复杂的任何布局方式整个childView旋转效果可以参考博客  http://www.jianshu.com/p/41e4602deca6 123 | 124 | 125 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | /build 3 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 29 5 | buildToolsVersion "29.0.2" 6 | defaultConfig { 7 | applicationId "chen.you.wheelviewtest" 8 | minSdkVersion 16 9 | targetSdkVersion 29 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 13 | } 14 | signingConfigs { 15 | debug { 16 | keyAlias 'you' 17 | keyPassword '123456' 18 | storeFile file('you.jks') 19 | storePassword '123456' 20 | } 21 | release { 22 | keyAlias 'you' 23 | keyPassword '123456' 24 | storeFile file('you.jks') 25 | storePassword '123456' 26 | } 27 | } 28 | 29 | buildTypes { 30 | debug { 31 | versionNameSuffix "-debug" 32 | minifyEnabled false 33 | zipAlignEnabled false 34 | shrinkResources false 35 | signingConfig signingConfigs.debug 36 | } 37 | 38 | release { 39 | //是否启动混淆 40 | minifyEnabled true 41 | //Zipalign优化 42 | zipAlignEnabled true 43 | // 移除无用的resource文件 44 | shrinkResources true 45 | signingConfig signingConfigs.release 46 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 47 | } 48 | 49 | android.applicationVariants.all { variant -> 50 | variant.outputs.all { 51 | outputFileName = "You_" + "v${variant.versionName}.apk" 52 | } 53 | } 54 | } 55 | } 56 | 57 | dependencies { 58 | implementation fileTree(dir: 'libs', include: ['*.jar']) 59 | implementation 'androidx.appcompat:appcompat:1.1.0' 60 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3' 61 | implementation 'androidx.recyclerview:recyclerview:1.1.0' 62 | testImplementation 'junit:junit:4.12' 63 | androidTestImplementation 'androidx.test.ext:junit:1.1.1' 64 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' 65 | implementation project(path: ':wheelview') 66 | } 67 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/chen/you/wheelviewtest/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package chen.you.wheelviewtest; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.platform.app.InstrumentationRegistry; 6 | import androidx.test.ext.junit.runners.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | 25 | assertEquals("chen.you.wheelviewtest", appContext.getPackageName()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/androidTest/java/you/xiaochen/wheel/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package you.xiaochen.wheel; 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("you.xiaochen.wheel", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/java/chen/you/wheelviewtest/MainActivity.java: -------------------------------------------------------------------------------- 1 | package chen.you.wheelviewtest; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.view.View; 6 | import android.widget.TextView; 7 | 8 | import androidx.appcompat.app.AppCompatActivity; 9 | 10 | import java.util.ArrayList; 11 | import java.util.Arrays; 12 | import java.util.List; 13 | 14 | import chen.you.wheel.WheelView; 15 | 16 | public class MainActivity extends AppCompatActivity { 17 | 18 | private WheelView wv_city, wv_county, wv_name; 19 | 20 | private CityAdapter cityAdapter; 21 | private CountyAdapter countyAdapter; 22 | 23 | private TextView tv_city, tv_county, tv_number; 24 | 25 | private WheelView wv_number; 26 | 27 | @Override 28 | protected void onCreate(Bundle savedInstanceState) { 29 | super.onCreate(savedInstanceState); 30 | setContentView(R.layout.activity_main); 31 | 32 | setTitle("游小陈的博客"); 33 | wv_city = findViewById(R.id.wv_city); 34 | wv_county = findViewById(R.id.wv_county); 35 | wv_number = findViewById(R.id.wv_number); 36 | wv_name = findViewById(R.id.wv_name); 37 | tv_city = findViewById(R.id.tv_city); 38 | tv_county = findViewById(R.id.tv_county); 39 | tv_number = findViewById(R.id.tv_number); 40 | 41 | 42 | /* 市滑轮控件 */ 43 | 44 | cityAdapter = new CityAdapter(); 45 | wv_city.setAdapter(cityAdapter); 46 | wv_city.addOnItemSelectedListener(new WheelView.OnItemSelectedListener() { 47 | @Override 48 | public void onItemSelected(WheelView wheelView, int index) { 49 | tv_city.setText("市: "+cityAdapter.getItem(index)); 50 | List strs = Arrays.asList(TestDatas.AREAS[index]); 51 | countyAdapter.strs.clear(); 52 | countyAdapter.strs.addAll(strs); 53 | countyAdapter.notifyDataSetChanged(); 54 | wv_county.setCurrentItem(0); 55 | tv_county.setText("县: "+countyAdapter.getItem(0)); 56 | } 57 | }); 58 | 59 | /* 区滑轮控件 */ 60 | 61 | wv_county.addOnItemSelectedListener(new WheelView.OnItemSelectedListener() { 62 | @Override 63 | public void onItemSelected(WheelView wheelView, int index) { 64 | tv_county.setText("县: "+countyAdapter.getItem(index)); 65 | } 66 | }); 67 | 68 | countyAdapter = new CountyAdapter(); 69 | wv_county.setAdapter(countyAdapter); 70 | 71 | /* 名字适配 */ 72 | wv_name.setAdapter(new WheelView.Adapter() { 73 | @Override 74 | public int getItemCount() { 75 | return 20; 76 | } 77 | 78 | @Override 79 | public String getItem(int position) { 80 | return "游小陈"; 81 | } 82 | }); 83 | 84 | /* 水平滑轮控件 */ 85 | wv_number.setAdapter(new WheelView.Adapter() { 86 | @Override 87 | public int getItemCount() { 88 | return 100; 89 | } 90 | 91 | @Override 92 | public String getItem(int position) { 93 | return String.valueOf(position); 94 | } 95 | }); 96 | wv_number.addOnItemSelectedListener(new WheelView.OnItemSelectedListener() { 97 | @Override 98 | public void onItemSelected(WheelView wheelView, int index) { 99 | tv_number.setText("水平布局"+index); 100 | } 101 | }); 102 | wv_number.setCurrentItem(88); 103 | 104 | findViewById(R.id.bt).setOnClickListener(new View.OnClickListener() { 105 | @Override 106 | public void onClick(View v) { 107 | startActivity(new Intent(MainActivity.this, TwoActivity.class)); 108 | } 109 | }); 110 | } 111 | 112 | 113 | 114 | private class CityAdapter extends WheelView.Adapter { 115 | @Override 116 | public int getItemCount() { 117 | return TestDatas.NAMES.length; 118 | } 119 | 120 | @Override 121 | public String getItem(int position) { 122 | return TestDatas.NAMES[position]; 123 | } 124 | } 125 | 126 | private class CountyAdapter extends WheelView.Adapter { 127 | 128 | private List strs; 129 | 130 | CountyAdapter() { 131 | strs = new ArrayList<>(); 132 | } 133 | 134 | @Override 135 | public int getItemCount() { 136 | return strs.size(); 137 | } 138 | 139 | @Override 140 | public String getItem(int position) { 141 | return strs.get(position); 142 | } 143 | } 144 | 145 | } 146 | -------------------------------------------------------------------------------- /app/src/main/java/chen/you/wheelviewtest/TestDatas.java: -------------------------------------------------------------------------------- 1 | package chen.you.wheelviewtest; 2 | 3 | /** 4 | * Created by you on 2017/9/20. 5 | */ 6 | 7 | public final class TestDatas { 8 | 9 | public static String[] NAMES = {"南昌", "赣州", "九江", "上饶", "宜春", "抚州", "吉安", "景德镇", "萍乡", "鹰潭"}; 10 | 11 | 12 | public static String[][] AREAS = {{"东湖区", "东湖区", "青云谱区", "湾里区", "青山湖区", "南昌县", "安义县", "进贤县"}, 13 | {"章贡区", 14 | "瑞金市", 15 | "南康市", 16 | "石城县", 17 | "安远县", 18 | "赣县", 19 | "宁都县", 20 | "寻乌县", 21 | "兴国县", 22 | "定南县", 23 | "上犹县", 24 | "于都县", 25 | "龙南县", 26 | "崇义县", 27 | "信丰县", 28 | "全南县", 29 | "大余县", 30 | "会昌县"}, 31 | {"浔阳区", 32 | "庐山区", 33 | "瑞昌市", 34 | "九江县", 35 | "星子县", 36 | "武宁县", 37 | "彭泽县", 38 | "永修县", 39 | "修水县", 40 | "湖口县", 41 | "德安县", 42 | "都昌县"}, 43 | {"信州区", 44 | "德兴市", 45 | "上饶县", 46 | "广丰县", 47 | "鄱阳县", 48 | "婺源县", 49 | "铅山县", 50 | "余干县", 51 | "横峰县", 52 | "弋阳县", 53 | "玉山县", 54 | "万年县"}, 55 | {"袁州区", 56 | "丰城市", 57 | "樟树市", 58 | "高安市", 59 | "铜鼓县", 60 | "靖安县", 61 | "宜丰县", 62 | "奉新县", 63 | "万载县", 64 | "上高县"}, 65 | {"临川区", 66 | "南丰县", 67 | "乐安县", 68 | "金溪县", 69 | "南城县", 70 | "东乡县", 71 | "资溪县", 72 | "宜黄县", 73 | "广昌县", 74 | "黎川县", 75 | "崇仁县"}, 76 | {"吉州区", 77 | "青原区", 78 | "井冈山市", 79 | "吉安县", 80 | "永丰县", 81 | "永新县", 82 | "新干县", 83 | "泰和县", 84 | "峡江县", 85 | "遂川县", 86 | "安福县", 87 | "吉水县", 88 | "万安县"}, 89 | {"珠山区", 90 | "昌江区", 91 | "乐平市", 92 | "浮梁县"}, 93 | {"安源区", 94 | "湘东区", 95 | "莲花县", 96 | "上栗县", 97 | "芦溪县"}, 98 | {"月湖区", 99 | "贵溪市", 100 | "余江县"} 101 | 102 | }; 103 | 104 | 105 | } 106 | -------------------------------------------------------------------------------- /app/src/main/java/chen/you/wheelviewtest/TwoActivity.java: -------------------------------------------------------------------------------- 1 | package chen.you.wheelviewtest; 2 | 3 | import android.graphics.Color; 4 | import android.os.Bundle; 5 | import android.view.View; 6 | 7 | import androidx.annotation.NonNull; 8 | import androidx.appcompat.app.AppCompatActivity; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | import chen.you.wheel.LinearDrawManager; 14 | import chen.you.wheel.WheelDrawManager; 15 | import chen.you.wheel.WheelParams; 16 | import chen.you.wheel.WheelView; 17 | 18 | /** 19 | * author: you : 2022/12/8 20 | */ 21 | public final class TwoActivity extends AppCompatActivity implements View.OnClickListener { 22 | 23 | private WheelView wv; 24 | 25 | private TestAdapter adapter; 26 | 27 | private List tests = new ArrayList<>(); 28 | 29 | @Override 30 | protected void onCreate(Bundle savedInstanceState) { 31 | super.onCreate(savedInstanceState); 32 | setContentView(R.layout.activity_two); 33 | wv = findViewById(R.id.wv_city); 34 | for (int i = 0; i < 100; i++) { 35 | tests.add(String.valueOf(i)); 36 | } 37 | adapter = new TestAdapter(tests); 38 | wv.setAdapter(adapter); 39 | wv.setCurrentItem(10); 40 | 41 | findViewById(R.id.bt0).setOnClickListener(this); 42 | findViewById(R.id.bt1).setOnClickListener(this); 43 | findViewById(R.id.bt2).setOnClickListener(this); 44 | findViewById(R.id.bt3).setOnClickListener(this); 45 | 46 | } 47 | 48 | @Override 49 | public void onClick(View v) { 50 | switch (v.getId()) { 51 | case R.id.bt0: 52 | tests.clear(); 53 | for (int i = 0; i < 100; i++) { 54 | tests.add("new test " + i); 55 | } 56 | adapter.notifyDataSetChanged(); 57 | break; 58 | case R.id.bt1: 59 | // WheelParams params0 = new WheelParams.Builder().setOrientation(WheelParams.HORIZONTAL).setTextCenterColor(Color.RED).build(); 60 | WheelParams params = wv.getWheelParams().newBuilder().setOrientation(WheelParams.HORIZONTAL).setTextCenterColor(Color.RED).build(); 61 | wv.setWheelParams(params); 62 | break; 63 | case R.id.bt2: 64 | if (wv.getDrawManager() instanceof LinearDrawManager) { 65 | wv.setDrawManager(new WheelDrawManager()); 66 | } else { 67 | wv.setDrawManager(new LinearDrawManager()); 68 | } 69 | break; 70 | case R.id.bt3: 71 | wv.setAdapter(new WheelView.Adapter() { 72 | @Override 73 | public int getItemCount() { 74 | return 100; 75 | } 76 | 77 | @NonNull 78 | @Override 79 | public String getItem(int position) { 80 | return "position " + position; 81 | } 82 | }); 83 | break; 84 | } 85 | } 86 | 87 | static class TestAdapter extends WheelView.Adapter { 88 | 89 | List strs; 90 | 91 | public TestAdapter(List strs) { 92 | this.strs = strs; 93 | } 94 | 95 | @Override 96 | public int getItemCount() { 97 | return strs.size(); 98 | } 99 | 100 | @Override 101 | public String getItem(int position) { 102 | return strs.get(position); 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/layout/act_test.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 9 | 10 | 15 | 16 | 20 | 21 | 25 | 26 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 21 | 22 | 29 | 30 | 37 | 38 | 39 | 40 | 45 | 46 | 50 | 51 | 55 | 56 | 61 | 62 | 63 | 64 | 74 | 75 | 85 | 86 | 96 | 97 | 98 | 99 | 106 | 107 | 117 | 118 |