├── app
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ ├── colors.xml
│ │ │ │ ├── dimens.xml
│ │ │ │ ├── styles.xml
│ │ │ │ └── attrs.xml
│ │ │ ├── drawable
│ │ │ │ ├── ic_close.png
│ │ │ │ └── ic_choose.png
│ │ │ ├── mipmap-hdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── anim
│ │ │ │ ├── dialog_enter_anim.xml
│ │ │ │ └── dialog_exit_anim.xml
│ │ │ ├── values-w820dp
│ │ │ │ └── dimens.xml
│ │ │ └── layout
│ │ │ │ ├── vp_item_address.xml
│ │ │ │ ├── layout_tab.xml
│ │ │ │ ├── vp_item.xml
│ │ │ │ ├── rv_item_address.xml
│ │ │ │ ├── activity_main.xml
│ │ │ │ └── dialog_choose_address.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── rudy
│ │ │ │ └── addresschoose
│ │ │ │ ├── bean
│ │ │ │ ├── Item.java
│ │ │ │ └── Province.java
│ │ │ │ ├── utils
│ │ │ │ └── FileUtils.java
│ │ │ │ ├── MainActivity.java
│ │ │ │ ├── adapter
│ │ │ │ ├── AddressAdapter.java
│ │ │ │ └── ViewPagerAdapter.java
│ │ │ │ ├── dialog
│ │ │ │ └── ChooseAddressDialog.java
│ │ │ │ └── widget
│ │ │ │ └── SlidingTabLayout.java
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── rudy
│ │ │ └── addresschoose
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── rudy
│ │ └── addresschoose
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── screenshot
├── activity.gif
└── dialog.gif
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .gitignore
├── README.md
├── gradle.properties
├── gradlew.bat
└── gradlew
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/screenshot/activity.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RudyJun/AddressChoose/HEAD/screenshot/activity.gif
--------------------------------------------------------------------------------
/screenshot/dialog.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RudyJun/AddressChoose/HEAD/screenshot/dialog.gif
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | 地址选择
3 |
4 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RudyJun/AddressChoose/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RudyJun/AddressChoose/HEAD/app/src/main/res/drawable/ic_close.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_choose.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RudyJun/AddressChoose/HEAD/app/src/main/res/drawable/ic_choose.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RudyJun/AddressChoose/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RudyJun/AddressChoose/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RudyJun/AddressChoose/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RudyJun/AddressChoose/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RudyJun/AddressChoose/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 | .externalNativeBuild
10 | /.idea
11 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/dialog_enter_anim.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/dialog_exit_anim.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AddressChoose
2 | 仿京东地址实现,可弹框
3 | ## Activity
4 | 
5 | ## Dialog
6 | 
7 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Dec 28 10:00:20 PST 2015
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-2.14.1-all.zip
7 |
--------------------------------------------------------------------------------
/app/src/main/java/com/rudy/addresschoose/bean/Item.java:
--------------------------------------------------------------------------------
1 | package com.rudy.addresschoose.bean;
2 |
3 | /**
4 | * Created by liuzaijun on 2017/8/16.
5 | */
6 |
7 | public class Item {
8 | public String itemName;
9 | public boolean isChoose;
10 |
11 | public Item(String itemName) {
12 | this.itemName = itemName;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/test/java/com/rudy/addresschoose/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.rudy.addresschoose;
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 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/vp_item_address.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
--------------------------------------------------------------------------------
/app/src/main/java/com/rudy/addresschoose/bean/Province.java:
--------------------------------------------------------------------------------
1 | package com.rudy.addresschoose.bean;
2 |
3 | import java.util.List;
4 |
5 | /**
6 | * Created by liuzaijun on 2017/8/16.
7 | */
8 |
9 | public class Province {
10 |
11 | public String name;
12 | public List array;
13 |
14 | public static class City {
15 | public String name;
16 | public List array;
17 | }
18 |
19 | public static class Region {
20 | public String name;
21 | public List array;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_tab.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/vp_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
17 |
18 |
--------------------------------------------------------------------------------
/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:\AndroidSDK/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/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/rudy/addresschoose/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.rudy.addresschoose;
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.giveu.addresschoose", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/rv_item_address.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
15 |
16 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/java/com/rudy/addresschoose/utils/FileUtils.java:
--------------------------------------------------------------------------------
1 | package com.rudy.addresschoose.utils;
2 |
3 | import android.content.Context;
4 |
5 | import java.io.BufferedReader;
6 | import java.io.InputStreamReader;
7 |
8 | /**
9 | * Created by liuzaijun on 2017/8/16.
10 | */
11 |
12 | public class FileUtils {
13 | /**
14 | * 从assets目录中读取文件
15 | *
16 | * @param context
17 | * @param fileName
18 | * @return
19 | */
20 | public static String getFromAssets(Context context, String fileName) {
21 | StringBuffer buffer = new StringBuffer();
22 | try {
23 | InputStreamReader inputReader = new InputStreamReader(context.getResources().getAssets().open(fileName));
24 | BufferedReader bufReader = new BufferedReader(inputReader);
25 | String line;
26 | while ((line = bufReader.readLine()) != null) {
27 | buffer.append(line);
28 | }
29 | } catch (Exception e) {
30 | e.printStackTrace();
31 | }
32 | return buffer.toString();
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 25
5 | buildToolsVersion "25.0.3"
6 | defaultConfig {
7 | applicationId "com.giveu.addresschoose"
8 | minSdkVersion 14
9 | targetSdkVersion 25
10 | versionCode 1
11 | versionName "1.0"
12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | sourceSets { main { res.srcDirs = ['src/main/res', 'src/main/res/anim'] } }
21 | }
22 |
23 | dependencies {
24 | compile fileTree(include: ['*.jar'], dir: 'libs')
25 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
26 | exclude group: 'com.android.support', module: 'support-annotations'
27 | })
28 | compile 'com.android.support:appcompat-v7:25.3.1'
29 | testCompile 'junit:junit:4.12'
30 | compile 'com.google.code.gson:gson:2.8.0'
31 | compile 'com.android.support:recyclerview-v7:25.0.0'
32 | }
33 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
15 |
16 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
15 |
19 |
20 |
34 |
39 |
40 |
41 |
42 |
48 |
49 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_choose_address.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
12 |
13 |
21 |
22 |
30 |
31 |
32 |
35 |
36 |
50 |
51 |
56 |
57 |
58 |
59 |
64 |
65 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/app/src/main/java/com/rudy/addresschoose/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.rudy.addresschoose;
2 |
3 | import android.os.Bundle;
4 | import android.support.v4.view.ViewPager;
5 | import android.support.v7.app.AppCompatActivity;
6 | import android.view.View;
7 | import android.widget.TextView;
8 |
9 | import com.google.gson.Gson;
10 | import com.google.gson.reflect.TypeToken;
11 | import com.rudy.addresschoose.adapter.ViewPagerAdapter;
12 | import com.rudy.addresschoose.bean.Province;
13 | import com.rudy.addresschoose.dialog.ChooseAddressDialog;
14 | import com.rudy.addresschoose.utils.FileUtils;
15 | import com.rudy.addresschoose.widget.SlidingTabLayout;
16 |
17 | import java.util.ArrayList;
18 |
19 | /**
20 | * Created by liuzaijun on 2017/8/17.
21 | */
22 | public class MainActivity extends AppCompatActivity {
23 |
24 | private ViewPager vpAddress;
25 | private SlidingTabLayout stCity;
26 | private ViewPagerAdapter pagerAdapter;
27 | private ArrayList provinces;
28 | private TextView tvAddress;
29 | private ChooseAddressDialog chooseAddressDialog;
30 |
31 |
32 | @Override
33 | protected void onCreate(Bundle savedInstanceState) {
34 | super.onCreate(savedInstanceState);
35 | setContentView(R.layout.activity_main);
36 | vpAddress = (ViewPager) findViewById(R.id.vp_address);
37 | tvAddress = (TextView) findViewById(R.id.tv_address);
38 | stCity = (SlidingTabLayout) findViewById(R.id.st_city);
39 | pagerAdapter = new ViewPagerAdapter(this, vpAddress);
40 | vpAddress.setAdapter(pagerAdapter);
41 | vpAddress.setOffscreenPageLimit(3);
42 | stCity.setViewPager(vpAddress);
43 |
44 | chooseAddressDialog = new ChooseAddressDialog(this);
45 | chooseAddressDialog.setOnAddressChooseListener(new ChooseAddressDialog.OnAddressChooseListener() {
46 | @Override
47 | public void onChoose(String province, String city, String region, String street) {
48 | tvAddress.setText(province + city + region + street + "(可点击)");
49 | }
50 | });
51 | tvAddress.setOnClickListener(new View.OnClickListener() {
52 | @Override
53 | public void onClick(View v) {
54 | chooseAddressDialog.show();
55 | }
56 | });
57 |
58 | //解析地址
59 | getAddressJson();
60 | pagerAdapter.setOnAddressChooseListener(new ViewPagerAdapter.OnAddressChooseListener() {
61 | @Override
62 | public void onChoose(String province, String city, String region, String street) {
63 | tvAddress.setText(province + city + region + street + "(可点击)");
64 | }
65 | });
66 | }
67 |
68 | public void getAddressJson() {
69 | //开启子线程处理读取地址并解析
70 | new Thread(new Runnable() {
71 | @Override
72 | public void run() {
73 | Gson gson = new Gson();
74 | provinces = gson.fromJson(FileUtils.getFromAssets(MainActivity.this, "addressJson.json"), new TypeToken>() {
75 | }.getType());
76 | initView();
77 | }
78 | }).start();
79 | }
80 |
81 | private void initView() {
82 | runOnUiThread(new Runnable() {
83 | @Override
84 | public void run() {
85 | //pagerAdapter、chooseAddressDialog初始化数据
86 | processData();
87 | }
88 | });
89 | }
90 |
91 | private void processData() {
92 | pagerAdapter.setData(provinces);
93 | chooseAddressDialog.setData(provinces);
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/app/src/main/java/com/rudy/addresschoose/adapter/AddressAdapter.java:
--------------------------------------------------------------------------------
1 | package com.rudy.addresschoose.adapter;
2 |
3 | import android.content.Context;
4 | import android.graphics.Color;
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.ImageView;
10 | import android.widget.TextView;
11 |
12 | import com.rudy.addresschoose.R;
13 | import com.rudy.addresschoose.bean.Item;
14 |
15 | import java.util.ArrayList;
16 |
17 | /**
18 | * Created by liuzaijun on 2017/8/17.
19 | */
20 |
21 | public class AddressAdapter extends RecyclerView.Adapter {
22 |
23 | private Context context;
24 | private ArrayList- items;
25 |
26 | public AddressAdapter(Context context, ArrayList
- items) {
27 | this.context = context;
28 | this.items = items;
29 | }
30 |
31 | @Override
32 | public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
33 | View itemView = LayoutInflater.from(context).inflate(R.layout.rv_item_address, parent, false);
34 | return new ViewHolder(itemView);
35 | }
36 |
37 | @Override
38 | public void onBindViewHolder(final ViewHolder holder, final int position) {
39 | holder.tvAddress.setText(items.get(position).itemName);
40 | if (items.get(position).isChoose) {
41 | holder.ivChoose.setVisibility(View.VISIBLE);
42 | holder.tvAddress.setTextColor(Color.parseColor("#ff3e3e"));
43 | } else {
44 | holder.ivChoose.setVisibility(View.GONE);
45 | holder.tvAddress.setTextColor(Color.parseColor("#4a4a4a"));
46 | }
47 |
48 | holder.getConvertView().setOnClickListener(new View.OnClickListener() {
49 | @Override
50 | public void onClick(View v) {
51 | //处理当前的点击事件,即勾选地址
52 | holder.ivChoose.setVisibility(View.VISIBLE);
53 | items.get(position).isChoose = true;
54 | for (Item item : items) {
55 | if (!item.itemName.equals(items.get(position).itemName) && item.isChoose) {
56 | item.isChoose = false;
57 | break;
58 | }
59 | }
60 | notifyDataSetChanged();
61 | if (listener != null) {
62 | listener.onItemClick(items.get(position).itemName, position);
63 | }
64 | }
65 | });
66 |
67 |
68 | }
69 |
70 | @Override
71 | public int getItemCount() {
72 | return items.size();
73 | }
74 |
75 | private OnItemClikListener listener;
76 |
77 | /**
78 | * 点击item的回调
79 | * @param listener
80 | */
81 | public void setOnItemClikListener(OnItemClikListener listener) {
82 | this.listener = listener;
83 | }
84 |
85 | public interface OnItemClikListener {
86 | void onItemClick(String itemName, int addressPos);
87 | }
88 |
89 | public static class ViewHolder extends RecyclerView.ViewHolder {
90 | public TextView tvAddress;
91 | public ImageView ivChoose;
92 |
93 | public ViewHolder(View itemView) {
94 | super(itemView);
95 | tvAddress = (TextView) itemView.findViewById(R.id.tv_address);
96 | ivChoose = (ImageView) itemView.findViewById(R.id.iv_choose);
97 | }
98 |
99 | public View getConvertView() {
100 | return itemView;
101 | }
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/app/src/main/java/com/rudy/addresschoose/dialog/ChooseAddressDialog.java:
--------------------------------------------------------------------------------
1 | package com.rudy.addresschoose.dialog;
2 |
3 | import android.app.Activity;
4 | import android.app.Dialog;
5 | import android.support.v4.view.ViewPager;
6 | import android.view.Gravity;
7 | import android.view.View;
8 | import android.view.Window;
9 | import android.view.WindowManager;
10 | import android.widget.ImageView;
11 |
12 | import com.rudy.addresschoose.R;
13 | import com.rudy.addresschoose.adapter.ViewPagerAdapter;
14 | import com.rudy.addresschoose.bean.Province;
15 | import com.rudy.addresschoose.widget.SlidingTabLayout;
16 |
17 | import java.util.ArrayList;
18 |
19 | /**
20 | * Created by liuzaijun on 2017/8/19.
21 | */
22 |
23 | public class ChooseAddressDialog extends Dialog {
24 | protected Activity mAttachActivity;
25 | private ViewPager vpAddress;
26 | private SlidingTabLayout stCity;
27 | private ViewPagerAdapter pagerAdapter;
28 | private ImageView ivClose;
29 |
30 | public ChooseAddressDialog(Activity context) {
31 | this(context, R.style.customerDialog);
32 | }
33 |
34 | public ChooseAddressDialog(Activity context, int themeResId) {
35 | super(context, themeResId);
36 | mAttachActivity = context;
37 | init();
38 | }
39 |
40 | private void init() {
41 | setContentView(R.layout.dialog_choose_address);
42 | vpAddress = (ViewPager) findViewById(R.id.vp_address);
43 | stCity = (SlidingTabLayout) findViewById(R.id.st_city);
44 | ivClose = (ImageView) findViewById(R.id.iv_close);
45 | ivClose.setOnClickListener(new View.OnClickListener() {
46 | @Override
47 | public void onClick(View v) {
48 | dismiss();
49 | }
50 | });
51 | pagerAdapter = new ViewPagerAdapter(mAttachActivity, vpAddress);
52 | vpAddress.setAdapter(pagerAdapter);
53 | vpAddress.setOffscreenPageLimit(3);
54 | stCity.setViewPager(vpAddress);
55 | //pagerAdapter的回调
56 | pagerAdapter.setOnAddressChooseListener(new ViewPagerAdapter.OnAddressChooseListener() {
57 | @Override
58 | public void onChoose(String province, String city, String region, String street) {
59 | if (listener != null) {
60 | //dialog的回调
61 | listener.onChoose(province, city, region, street);
62 | }
63 | //延迟消失时为了显示选择完成时可以看到选择了哪项
64 | vpAddress.postDelayed(new Runnable() {
65 | @Override
66 | public void run() {
67 | dismiss();
68 | }
69 | }, 100);
70 | }
71 | });
72 |
73 | Window window = getWindow();
74 | if (window != null) {
75 | WindowManager.LayoutParams attributes = window.getAttributes();
76 | attributes.height = (int) (getScreenHeight() * (0.56));
77 | attributes.width = getScreenWidth();
78 | attributes.gravity = Gravity.BOTTOM;
79 | window.setAttributes(attributes);
80 | window.setWindowAnimations(R.style.dialogWindowAnim); //设置窗口弹出动画
81 | }
82 | setCancelable(false);
83 | setCanceledOnTouchOutside(false);
84 | }
85 |
86 | /**
87 | * 初始化数据
88 | *
89 | * @param provinces
90 | */
91 | public void setData(ArrayList provinces) {
92 | pagerAdapter.setData(provinces);
93 | }
94 |
95 | private OnAddressChooseListener listener;
96 |
97 | /**
98 | * 地址选择完成的回调
99 | *
100 | * @param listener
101 | */
102 | public void setOnAddressChooseListener(OnAddressChooseListener listener) {
103 | this.listener = listener;
104 | }
105 |
106 | public interface OnAddressChooseListener {
107 | void onChoose(String province, String city, String region, String street);
108 | }
109 |
110 | /**
111 | * 获取屏幕宽度
112 | */
113 | public int getScreenWidth() {
114 | return mAttachActivity.getResources().getDisplayMetrics().widthPixels;
115 | }
116 |
117 | /**
118 | * 获取屏幕高度
119 | */
120 | public int getScreenHeight() {
121 | return mAttachActivity.getResources().getDisplayMetrics().heightPixels;
122 | }
123 |
124 | }
125 |
--------------------------------------------------------------------------------
/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 |
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 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/app/src/main/java/com/rudy/addresschoose/adapter/ViewPagerAdapter.java:
--------------------------------------------------------------------------------
1 | package com.rudy.addresschoose.adapter;
2 |
3 | import android.content.Context;
4 | import android.support.v4.view.PagerAdapter;
5 | import android.support.v4.view.ViewPager;
6 | import android.support.v7.widget.LinearLayoutManager;
7 | import android.support.v7.widget.RecyclerView;
8 | import android.view.View;
9 | import android.view.ViewGroup;
10 |
11 | import com.rudy.addresschoose.R;
12 | import com.rudy.addresschoose.bean.Item;
13 | import com.rudy.addresschoose.bean.Province;
14 |
15 | import java.util.ArrayList;
16 | /**
17 | * Created by liuzaijun on 2017/8/17.
18 | */
19 | public class ViewPagerAdapter extends PagerAdapter {
20 |
21 | private ArrayList tabList;
22 | private Context context;
23 | private ArrayList provinces;
24 | private ArrayList> itemList;
25 | private ViewPager mViewPager;
26 | private int pPosition = -1;
27 | private int cPosition = -1;
28 | private int rPosition = -1;
29 |
30 | public ViewPagerAdapter(Context context, ViewPager viewPager) {
31 | this.context = context;
32 | this.tabList = new ArrayList<>();
33 | this.itemList = new ArrayList<>();
34 | this.mViewPager = viewPager;
35 | if (viewPager == null) {
36 | throw new IllegalArgumentException("viewPager is null , please check");
37 | }
38 | }
39 |
40 | /**
41 | * 省列表,其中每一个省下又有市、区、街道(可能有也可能没有)
42 | *
43 | * @param provinces
44 | */
45 | public void setData(ArrayList provinces) {
46 | if (provinces == null) {
47 | return;
48 | }
49 | this.provinces = provinces;
50 | ArrayList
- provinceItem = new ArrayList<>();
51 | //遍历provinces,得到省
52 | for (Province province : provinces) {
53 | Item item = new Item(province.name);
54 | provinceItem.add(item);
55 | }
56 | //添加省列表
57 | itemList.add(provinceItem);
58 | //添加一个tab
59 | tabList.add("请选择");
60 | //调用notifyDataSetChanged()方法,以便刷新tab的内容(最开始tab和viewpager是没有内容的)
61 | notifyDataSetChanged();
62 | }
63 |
64 | @Override
65 | public int getCount() {
66 | return tabList.size();
67 | }
68 |
69 | @Override
70 | public boolean isViewFromObject(View view, Object object) {
71 | return view == object;
72 | }
73 |
74 | @Override
75 | public void destroyItem(ViewGroup container, int position, Object object) {
76 | container.removeView((View) object);
77 | }
78 |
79 | @Override
80 | public View instantiateItem(ViewGroup container, final int position) {
81 | View view = View.inflate(context, R.layout.vp_item_address, null);
82 | view.setTag(position);
83 | //rvAddress用来填充具体的省、市、区、街道(可能是没有的,得看选中的地区)
84 | RecyclerView rvAddress = (RecyclerView) view.findViewById(R.id.rv_address);
85 | //布局管理器
86 | LinearLayoutManager layoutManager = new LinearLayoutManager(context);
87 | layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
88 | rvAddress.setLayoutManager(layoutManager);
89 | //适配器
90 | AddressAdapter addressAdapter = new AddressAdapter(context, itemList.get(position));
91 | rvAddress.setAdapter(addressAdapter);
92 | addressAdapter.setOnItemClikListener(new AddressAdapter.OnItemClikListener() {
93 | @Override
94 | public void onItemClick(String itemName, int addressPos) {
95 | //替换tab的内容,如原来显示的tab是请选择,选中广东省后,替换position为0所对应的string为广东省
96 | replaceStr(position, itemName);
97 | //这个list用来存储下一级列表的内容,如广东省下的市
98 | ArrayList
- cityItem = new ArrayList<>();
99 | //标识此次选择的省、市、区、街道与上次选择的是否一样
100 | boolean isChange = false;
101 | switch (position) {
102 | case 0:
103 | //根据选中的位置判断是否选中的是同一个省或同一个市...其他case也是如此
104 | if (addressPos != pPosition) {
105 | //此次选择与上次不同,需要截断之后的内容,如上次选择广东省,这次选择北京
106 | //那么需要截断广东省后的市、区、街道,即删除省之后的选择界面,从重置省之后的选中position
107 | truncateList(position + 1);
108 | isChange = true;
109 | cPosition = -1;
110 | rPosition = -1;
111 | }
112 | pPosition = addressPos;//记录该次选择省的位置
113 | //遍历得到该省下的市级列表
114 | for (Province.City city : provinces.get(pPosition).array) {
115 | Item item = new Item(city.name);
116 | cityItem.add(item);
117 | }
118 | break;
119 | case 1:
120 | if (addressPos != cPosition) {
121 | truncateList(position + 1);
122 | isChange = true;
123 | rPosition = -1;
124 | }
125 | cPosition = addressPos;
126 | for (Province.Region region : provinces.get(pPosition).array.get(cPosition).array) {
127 | Item item = new Item(region.name);
128 | cityItem.add(item);
129 | }
130 | break;
131 | case 2:
132 | if (addressPos != rPosition) {
133 | truncateList(position + 1);
134 | isChange = true;
135 | }
136 | rPosition = addressPos;
137 | //该区是否有街道的列表
138 | if (provinces.get(pPosition).array.get(cPosition).array.get(rPosition).array != null
139 | && !provinces.get(pPosition).array.get(cPosition).array.get(rPosition).array.isEmpty()) {
140 | for (String street : provinces.get(pPosition).array.get(cPosition).array.get(rPosition).array) {
141 | Item item = new Item(street);
142 | cityItem.add(item);
143 | }
144 | }
145 | break;
146 | case 3:
147 | break;
148 | }
149 | dealDataAndScroll(isChange, cityItem, position + 1);
150 | }
151 | });
152 | container.addView(view);
153 | return view;
154 | }
155 |
156 | private void truncateList(int fromIndex) {
157 | if (tabList.size() > fromIndex) {
158 | tabList.subList(fromIndex, tabList.size()).clear();
159 | itemList.subList(fromIndex, itemList.size()).clear();
160 | }
161 | }
162 |
163 | /**
164 | * 替换该postion对应的string
165 | *
166 | * @param position
167 | * @param itemName
168 | */
169 | private void replaceStr(int position, String itemName) {
170 | tabList.remove(position);
171 | tabList.add(position, itemName);
172 | }
173 |
174 | /**
175 | * @param isChange 与上次的选择是否一致
176 | * @param cityItem 存储下一级地区的list
177 | * @param position 需要滑动至viewpager的哪个位置
178 | */
179 | private void dealDataAndScroll(boolean isChange, ArrayList
- cityItem, final int position) {
180 | //判断cityItem是否为空,不为空说明还有下一级列表,否则说明地区选择完成
181 | if (!cityItem.isEmpty()) {
182 | //如果与上次选择不一致,之前已经做了内容的清除,现在新增一个tab
183 | if (isChange) {
184 | tabList.add("请选择");
185 | itemList.add(cityItem);
186 | //通知adapter,数据源已发生改变,需要刷新界面
187 | notifyDataSetChanged();
188 | }
189 | //延时滑动,以便有简短的事件可以看到选中的项
190 | mViewPager.postDelayed(new Runnable() {
191 | @Override
192 | public void run() {
193 | mViewPager.setCurrentItem(position);
194 | }
195 | }, 100);
196 | } else {
197 | notifyDataSetChanged();
198 | //地址选择完成
199 | if (listener != null) {
200 | String province = "";
201 | String city = "";
202 | String region = "";
203 | String street = "";
204 | for (int i = 0; i < tabList.size(); i++) {
205 | String str = tabList.get(i);
206 | switch (i) {
207 | case 0:
208 | province = str;
209 | break;
210 | case 1:
211 | city = str;
212 |
213 | break;
214 | case 2:
215 | region = str;
216 |
217 | break;
218 | case 3:
219 | street = str;
220 | break;
221 | }
222 | }
223 | listener.onChoose(province, city, region, street);
224 | }
225 | }
226 | }
227 |
228 | private OnAddressChooseListener listener;
229 |
230 | /**
231 | * 地址选择完成的回调
232 | *
233 | * @param listener
234 | */
235 | public void setOnAddressChooseListener(OnAddressChooseListener listener) {
236 | this.listener = listener;
237 | }
238 |
239 | public interface OnAddressChooseListener {
240 | void onChoose(String province, String city, String region, String street);
241 | }
242 |
243 | @Override
244 | public int getItemPosition(Object object) {
245 | View view = (View) object;
246 | int position = (int) view.getTag();
247 | //viewpager的局部刷新,想了解原理看请源码或资料
248 | if (position > mViewPager.getCurrentItem()) {
249 | //需要重绘
250 | return POSITION_NONE;
251 | } else {
252 | //不需要重绘
253 | return POSITION_UNCHANGED;
254 | }
255 | }
256 |
257 | @Override
258 | public CharSequence getPageTitle(int position) {
259 | return tabList.get(position);
260 | }
261 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rudy/addresschoose/widget/SlidingTabLayout.java:
--------------------------------------------------------------------------------
1 | package com.rudy.addresschoose.widget;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.database.DataSetObserver;
6 | import android.graphics.Canvas;
7 | import android.graphics.Color;
8 | import android.graphics.Paint;
9 | import android.graphics.Path;
10 | import android.graphics.Rect;
11 | import android.graphics.drawable.GradientDrawable;
12 | import android.os.Bundle;
13 | import android.os.Parcelable;
14 | import android.support.annotation.Nullable;
15 | import android.support.v4.app.Fragment;
16 | import android.support.v4.app.FragmentActivity;
17 | import android.support.v4.app.FragmentManager;
18 | import android.support.v4.app.FragmentPagerAdapter;
19 | import android.support.v4.view.PagerAdapter;
20 | import android.support.v4.view.ViewPager;
21 | import android.util.AttributeSet;
22 | import android.util.SparseArray;
23 | import android.util.TypedValue;
24 | import android.view.Gravity;
25 | import android.view.View;
26 | import android.view.ViewGroup;
27 | import android.widget.HorizontalScrollView;
28 | import android.widget.LinearLayout;
29 | import android.widget.TextView;
30 |
31 | import com.rudy.addresschoose.R;
32 |
33 | import java.util.ArrayList;
34 | import java.util.Collections;
35 |
36 | /**
37 | * 滑动TabLayout,对于ViewPager的依赖性强
38 | */
39 | public class SlidingTabLayout extends HorizontalScrollView implements ViewPager.OnPageChangeListener {
40 | private Context mContext;
41 | private ViewPager mViewPager;
42 | private ArrayList mTitles;
43 | private LinearLayout mTabsContainer;
44 | private int mCurrentTab;
45 | private float mCurrentPositionOffset;
46 | private int mTabCount;
47 | /**
48 | * 用于绘制显示器
49 | */
50 | private Rect mIndicatorRect = new Rect();
51 | /**
52 | * 用于实现滚动居中
53 | */
54 | private Rect mTabRect = new Rect();
55 | private GradientDrawable mIndicatorDrawable = new GradientDrawable();
56 |
57 | private Paint mRectPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
58 | private Paint mDividerPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
59 | private Paint mTrianglePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
60 | private Path mTrianglePath = new Path();
61 | private static final int STYLE_NORMAL = 0;
62 | private static final int STYLE_TRIANGLE = 1;
63 | private static final int STYLE_BLOCK = 2;
64 | private int mIndicatorStyle = STYLE_NORMAL;
65 |
66 | private float mTabPadding;
67 | private boolean mTabSpaceEqual;
68 | private float mTabWidth;
69 |
70 | /**
71 | * indicator
72 | */
73 | private int mIndicatorColor;
74 | private float mIndicatorHeight;
75 | private float mIndicatorWidth;
76 | private float mIndicatorCornerRadius;
77 | private float mIndicatorMarginLeft;
78 | private float mIndicatorMarginTop;
79 | private float mIndicatorMarginRight;
80 | private float mIndicatorMarginBottom;
81 | private int mIndicatorGravity;
82 | private boolean mIndicatorWidthEqualTitle;
83 |
84 | /**
85 | * underline
86 | */
87 | private int mUnderlineColor;
88 | private float mUnderlineHeight;
89 | private int mUnderlineGravity;
90 |
91 | /**
92 | * divider
93 | */
94 | private int mDividerColor;
95 | private float mDividerWidth;
96 | private float mDividerPadding;
97 |
98 | /**
99 | * title
100 | */
101 | private static final int TEXT_BOLD_NONE = 0;
102 | private static final int TEXT_BOLD_WHEN_SELECT = 1;
103 | private static final int TEXT_BOLD_BOTH = 2;
104 | private float mTextsize;
105 | private int mTextSelectColor;
106 | private int mTextUnselectColor;
107 | private int mTextBold;
108 | private boolean mTextAllCaps;
109 |
110 | private int mLastScrollX;
111 | private int mHeight;
112 | private boolean mSnapOnTabClick;
113 |
114 | public SlidingTabLayout(Context context) {
115 | this(context, null, 0);
116 | }
117 |
118 | public SlidingTabLayout(Context context, AttributeSet attrs) {
119 | this(context, attrs, 0);
120 | }
121 |
122 | public SlidingTabLayout(Context context, AttributeSet attrs, int defStyleAttr) {
123 | super(context, attrs, defStyleAttr);
124 | setFillViewport(true);//设置滚动视图是否可以伸缩其内容以填充视口
125 | setWillNotDraw(false);//重写onDraw方法,需要调用这个方法来清除flag
126 | setClipChildren(false);
127 | setClipToPadding(false);
128 |
129 | this.mContext = context;
130 | mTabsContainer = new LinearLayout(context);
131 | addView(mTabsContainer);
132 |
133 | obtainAttributes(context, attrs);
134 |
135 | //get layout_height
136 | String height = attrs.getAttributeValue("http://schemas.android.com/apk/res/android", "layout_height");
137 |
138 | if (height.equals(ViewGroup.LayoutParams.MATCH_PARENT + "")) {
139 | } else if (height.equals(ViewGroup.LayoutParams.WRAP_CONTENT + "")) {
140 | } else {
141 | int[] systemAttrs = {android.R.attr.layout_height};
142 | TypedArray a = context.obtainStyledAttributes(attrs, systemAttrs);
143 | mHeight = a.getDimensionPixelSize(0, ViewGroup.LayoutParams.WRAP_CONTENT);
144 | a.recycle();
145 | }
146 | }
147 |
148 | private void obtainAttributes(Context context, AttributeSet attrs) {
149 | TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.SlidingTabLayout);
150 |
151 | mIndicatorStyle = ta.getInt(R.styleable.SlidingTabLayout_tl_indicator_style, STYLE_NORMAL);
152 | mIndicatorColor = ta.getColor(R.styleable.SlidingTabLayout_tl_indicator_color, Color.parseColor(mIndicatorStyle == STYLE_BLOCK ? "#4B6A87" : "#ffffff"));
153 | mIndicatorHeight = ta.getDimension(R.styleable.SlidingTabLayout_tl_indicator_height,
154 | dp2px(mIndicatorStyle == STYLE_TRIANGLE ? 4 : (mIndicatorStyle == STYLE_BLOCK ? -1 : 2)));
155 | mIndicatorWidth = ta.getDimension(R.styleable.SlidingTabLayout_tl_indicator_width, dp2px(mIndicatorStyle == STYLE_TRIANGLE ? 10 : -1));
156 | mIndicatorCornerRadius = ta.getDimension(R.styleable.SlidingTabLayout_tl_indicator_corner_radius, dp2px(mIndicatorStyle == STYLE_BLOCK ? -1 : 0));
157 | mIndicatorMarginLeft = ta.getDimension(R.styleable.SlidingTabLayout_tl_indicator_margin_left, dp2px(0));
158 | mIndicatorMarginTop = ta.getDimension(R.styleable.SlidingTabLayout_tl_indicator_margin_top, dp2px(mIndicatorStyle == STYLE_BLOCK ? 7 : 0));
159 | mIndicatorMarginRight = ta.getDimension(R.styleable.SlidingTabLayout_tl_indicator_margin_right, dp2px(0));
160 | mIndicatorMarginBottom = ta.getDimension(R.styleable.SlidingTabLayout_tl_indicator_margin_bottom, dp2px(mIndicatorStyle == STYLE_BLOCK ? 7 : 0));
161 | mIndicatorGravity = ta.getInt(R.styleable.SlidingTabLayout_tl_indicator_gravity, Gravity.BOTTOM);
162 | mIndicatorWidthEqualTitle = ta.getBoolean(R.styleable.SlidingTabLayout_tl_indicator_width_equal_title, false);
163 |
164 | mUnderlineColor = ta.getColor(R.styleable.SlidingTabLayout_tl_underline_color, Color.parseColor("#ffffff"));
165 | mUnderlineHeight = ta.getDimension(R.styleable.SlidingTabLayout_tl_underline_height, dp2px(0));
166 | mUnderlineGravity = ta.getInt(R.styleable.SlidingTabLayout_tl_underline_gravity, Gravity.BOTTOM);
167 |
168 | mDividerColor = ta.getColor(R.styleable.SlidingTabLayout_tl_divider_color, Color.parseColor("#ffffff"));
169 | mDividerWidth = ta.getDimension(R.styleable.SlidingTabLayout_tl_divider_width, dp2px(0));
170 | mDividerPadding = ta.getDimension(R.styleable.SlidingTabLayout_tl_divider_padding, dp2px(12));
171 |
172 | mTextsize = ta.getDimension(R.styleable.SlidingTabLayout_tl_textsize, sp2px(14));
173 | mTextSelectColor = ta.getColor(R.styleable.SlidingTabLayout_tl_textSelectColor, Color.parseColor("#ffffff"));
174 | mTextUnselectColor = ta.getColor(R.styleable.SlidingTabLayout_tl_textUnselectColor, Color.parseColor("#AAffffff"));
175 | mTextBold = ta.getInt(R.styleable.SlidingTabLayout_tl_textBold, TEXT_BOLD_NONE);
176 | mTextAllCaps = ta.getBoolean(R.styleable.SlidingTabLayout_tl_textAllCaps, false);
177 |
178 | mTabSpaceEqual = ta.getBoolean(R.styleable.SlidingTabLayout_tl_tab_space_equal, false);
179 | mTabWidth = ta.getDimension(R.styleable.SlidingTabLayout_tl_tab_width, dp2px(-1));
180 | mTabPadding = ta.getDimension(R.styleable.SlidingTabLayout_tl_tab_padding, mTabSpaceEqual || mTabWidth > 0 ? dp2px(0) : dp2px(20));
181 |
182 | ta.recycle();
183 | }
184 |
185 | /**
186 | * 关联ViewPager
187 | */
188 | public void setViewPager(ViewPager vp) {
189 | if (vp == null || vp.getAdapter() == null) {
190 | throw new IllegalStateException("ViewPager or ViewPager adapter can not be NULL !");
191 | }
192 |
193 | this.mViewPager = vp;
194 |
195 | this.mViewPager.removeOnPageChangeListener(this);
196 | this.mViewPager.addOnPageChangeListener(this);
197 | PagerAdapter adapter = this.mViewPager.getAdapter();
198 | setPagerAdapter(adapter, true);
199 | }
200 |
201 | private PagerAdapter mPagerAdapter;
202 | private DataSetObserver mPagerAdapterObserver;
203 |
204 | private void setPagerAdapter(@Nullable final PagerAdapter adapter, final boolean addObserver) {
205 | if (mPagerAdapter != null && mPagerAdapterObserver != null) {
206 | // If we already have a PagerAdapter, unregister our observer
207 | mPagerAdapter.unregisterDataSetObserver(mPagerAdapterObserver);
208 | }
209 |
210 | mPagerAdapter = adapter;
211 |
212 | if (addObserver && adapter != null) {
213 | // Register our observer on the new adapter
214 | if (mPagerAdapterObserver == null) {
215 | mPagerAdapterObserver = new PagerAdapterObserver();
216 | }
217 | adapter.registerDataSetObserver(mPagerAdapterObserver);
218 | }
219 |
220 | // Finally make sure we reflect the new adapter
221 | notifyDataSetChanged();
222 | }
223 |
224 | private class PagerAdapterObserver extends DataSetObserver {
225 | PagerAdapterObserver() {
226 | }
227 |
228 | @Override
229 | public void onChanged() {
230 | notifyDataSetChanged();
231 | }
232 |
233 | @Override
234 | public void onInvalidated() {
235 | notifyDataSetChanged();
236 | }
237 | }
238 |
239 | /**
240 | * 关联ViewPager,用于不想在ViewPager适配器中设置titles数据的情况
241 | */
242 | public void setViewPager(ViewPager vp, String[] titles) {
243 | if (vp == null || vp.getAdapter() == null) {
244 | throw new IllegalStateException("ViewPager or ViewPager adapter can not be NULL !");
245 | }
246 |
247 | if (titles == null || titles.length == 0) {
248 | throw new IllegalStateException("Titles can not be EMPTY !");
249 | }
250 |
251 | if (titles.length != vp.getAdapter().getCount()) {
252 | throw new IllegalStateException("Titles length must be the same as the page count !");
253 | }
254 |
255 | this.mViewPager = vp;
256 | mTitles = new ArrayList<>();
257 | Collections.addAll(mTitles, titles);
258 |
259 | this.mViewPager.removeOnPageChangeListener(this);
260 | this.mViewPager.addOnPageChangeListener(this);
261 | notifyDataSetChanged();
262 | }
263 |
264 | /**
265 | * 关联ViewPager,用于连适配器都不想自己实例化的情况
266 | */
267 | public void setViewPager(ViewPager vp, String[] titles, FragmentActivity fa, ArrayList fragments) {
268 | if (vp == null) {
269 | throw new IllegalStateException("ViewPager can not be NULL !");
270 | }
271 |
272 | if (titles == null || titles.length == 0) {
273 | throw new IllegalStateException("Titles can not be EMPTY !");
274 | }
275 |
276 | this.mViewPager = vp;
277 | this.mViewPager.setAdapter(new InnerPagerAdapter(fa.getSupportFragmentManager(), fragments, titles));
278 |
279 | this.mViewPager.removeOnPageChangeListener(this);
280 | this.mViewPager.addOnPageChangeListener(this);
281 | notifyDataSetChanged();
282 | }
283 |
284 | /**
285 | * 更新数据
286 | */
287 | public void notifyDataSetChanged() {
288 | mTabsContainer.removeAllViews();
289 | this.mTabCount = mTitles == null ? mViewPager.getAdapter().getCount() : mTitles.size();
290 | View tabView;
291 | for (int i = 0; i < mTabCount; i++) {
292 | tabView = View.inflate(mContext, R.layout.layout_tab, null);
293 | CharSequence pageTitle = mTitles == null ? mViewPager.getAdapter().getPageTitle(i) : mTitles.get(i);
294 | addTab(i, pageTitle.toString(), tabView);
295 | }
296 | updateTabStyles();
297 | post(new Runnable() {
298 | @Override
299 | public void run() {
300 | fullScroll(FOCUS_RIGHT);
301 | }
302 | });
303 | }
304 |
305 | public void addNewTab(String title) {
306 | View tabView = View.inflate(mContext, R.layout.layout_tab, null);
307 | if (mTitles != null) {
308 | mTitles.add(title);
309 | }
310 |
311 | CharSequence pageTitle = mTitles == null ? mViewPager.getAdapter().getPageTitle(mTabCount) : mTitles.get(mTabCount);
312 | addTab(mTabCount, pageTitle.toString(), tabView);
313 | this.mTabCount = mTitles == null ? mViewPager.getAdapter().getCount() : mTitles.size();
314 |
315 | updateTabStyles();
316 | }
317 |
318 | /**
319 | * 创建并添加tab
320 | */
321 | private void addTab(final int position, String title, View tabView) {
322 | TextView tv_tab_title = (TextView) tabView.findViewById(R.id.tv_tab_title);
323 | if (tv_tab_title != null) {
324 | if (title != null) tv_tab_title.setText(title);
325 | }
326 |
327 | tabView.setOnClickListener(new OnClickListener() {
328 | @Override
329 | public void onClick(View v) {
330 | int position = mTabsContainer.indexOfChild(v);
331 | if (position != -1) {
332 | if (mViewPager.getCurrentItem() != position) {
333 | if (mSnapOnTabClick) {
334 | mViewPager.setCurrentItem(position, false);
335 | } else {
336 | mViewPager.setCurrentItem(position);
337 | }
338 |
339 | if (mListener != null) {
340 | mListener.onTabSelect(position);
341 | }
342 | } else {
343 | if (mListener != null) {
344 | mListener.onTabReselect(position);
345 | }
346 | }
347 | }
348 | }
349 | });
350 |
351 | /** 每一个Tab的布局参数 */
352 | LinearLayout.LayoutParams lp_tab = mTabSpaceEqual ?
353 | new LinearLayout.LayoutParams(0, LayoutParams.MATCH_PARENT, 1.0f) :
354 | new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
355 | if (mTabWidth > 0) {
356 | lp_tab = new LinearLayout.LayoutParams((int) mTabWidth, LayoutParams.MATCH_PARENT);
357 | }
358 |
359 | mTabsContainer.addView(tabView, position, lp_tab);
360 | }
361 |
362 | private void updateTabStyles() {
363 | for (int i = 0; i < mTabCount; i++) {
364 | View v = mTabsContainer.getChildAt(i);
365 | // v.setPadding((int) mTabPadding, v.getPaddingTop(), (int) mTabPadding, v.getPaddingBottom());
366 | TextView tv_tab_title = (TextView) v.findViewById(R.id.tv_tab_title);
367 | if (tv_tab_title != null) {
368 | tv_tab_title.setTextColor(i == mCurrentTab ? mTextSelectColor : mTextUnselectColor);
369 | tv_tab_title.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextsize);
370 | tv_tab_title.setPadding((int) mTabPadding, 0, (int) mTabPadding, 0);
371 | if (mTextAllCaps) {
372 | tv_tab_title.setText(tv_tab_title.getText().toString().toUpperCase());
373 | }
374 |
375 | if (mTextBold == TEXT_BOLD_BOTH) {
376 | tv_tab_title.getPaint().setFakeBoldText(true);
377 | } else if (mTextBold == TEXT_BOLD_NONE) {
378 | tv_tab_title.getPaint().setFakeBoldText(false);
379 | }
380 | }
381 | }
382 | }
383 |
384 | @Override
385 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
386 | /**
387 | * position:当前View的位置
388 | * mCurrentPositionOffset:当前View的偏移量比例.[0,1)
389 | */
390 | this.mCurrentTab = position;
391 | this.mCurrentPositionOffset = positionOffset;
392 | scrollToCurrentTab();
393 | invalidate();
394 | }
395 |
396 | @Override
397 | public void onPageSelected(int position) {
398 | updateTabSelection(position);
399 | }
400 |
401 | @Override
402 | public void onPageScrollStateChanged(int state) {
403 | }
404 |
405 | /**
406 | * HorizontalScrollView滚到当前tab,并且居中显示
407 | */
408 | private void scrollToCurrentTab() {
409 | if (mTabCount <= 0) {
410 | return;
411 | }
412 |
413 | int offset = (int) (mCurrentPositionOffset * mTabsContainer.getChildAt(mCurrentTab).getWidth());
414 | /**当前Tab的left+当前Tab的Width乘以positionOffset*/
415 | int newScrollX = mTabsContainer.getChildAt(mCurrentTab).getLeft() + offset;
416 |
417 | if (mCurrentTab > 0 || offset > 0) {
418 | /**HorizontalScrollView移动到当前tab,并居中*/
419 | newScrollX -= getWidth() / 2 - getPaddingLeft();
420 | calcIndicatorRect();
421 | newScrollX += ((mTabRect.right - mTabRect.left) / 2);
422 | }
423 |
424 | if (newScrollX != mLastScrollX) {
425 | mLastScrollX = newScrollX;
426 | /** scrollTo(int x,int y):x,y代表的不是坐标点,而是偏移量
427 | * x:表示离起始位置的x水平方向的偏移量
428 | * y:表示离起始位置的y垂直方向的偏移量
429 | */
430 | scrollTo(newScrollX, 0);
431 | }
432 | }
433 |
434 | private void updateTabSelection(int position) {
435 | for (int i = 0; i < mTabCount; ++i) {
436 | View tabView = mTabsContainer.getChildAt(i);
437 | final boolean isSelect = i == position;
438 | TextView tab_title = (TextView) tabView.findViewById(R.id.tv_tab_title);
439 |
440 | if (tab_title != null) {
441 | tab_title.setTextColor(isSelect ? mTextSelectColor : mTextUnselectColor);
442 | if (mTextBold == TEXT_BOLD_WHEN_SELECT) {
443 | tab_title.getPaint().setFakeBoldText(isSelect);
444 | }
445 | }
446 | }
447 | }
448 |
449 | private float margin;
450 |
451 | private void calcIndicatorRect() {
452 | View currentTabView = mTabsContainer.getChildAt(this.mCurrentTab);
453 | float left = currentTabView.getLeft();
454 | float right = currentTabView.getRight();
455 |
456 | //for mIndicatorWidthEqualTitle
457 | if (mIndicatorStyle == STYLE_NORMAL && mIndicatorWidthEqualTitle) {
458 | TextView tab_title = (TextView) currentTabView.findViewById(R.id.tv_tab_title);
459 | mTextPaint.setTextSize(mTextsize);
460 | float textWidth = mTextPaint.measureText(tab_title.getText().toString());
461 | margin = (right - left - textWidth) / 2;
462 | }
463 |
464 | if (this.mCurrentTab < mTabCount - 1) {
465 | View nextTabView = mTabsContainer.getChildAt(this.mCurrentTab + 1);
466 | float nextTabLeft = nextTabView.getLeft();
467 | float nextTabRight = nextTabView.getRight();
468 |
469 | left = left + mCurrentPositionOffset * (nextTabLeft - left);
470 | right = right + mCurrentPositionOffset * (nextTabRight - right);
471 |
472 | //for mIndicatorWidthEqualTitle
473 | if (mIndicatorStyle == STYLE_NORMAL && mIndicatorWidthEqualTitle) {
474 | TextView next_tab_title = (TextView) nextTabView.findViewById(R.id.tv_tab_title);
475 | mTextPaint.setTextSize(mTextsize);
476 | float nextTextWidth = mTextPaint.measureText(next_tab_title.getText().toString());
477 | float nextMargin = (nextTabRight - nextTabLeft - nextTextWidth) / 2;
478 | margin = margin + mCurrentPositionOffset * (nextMargin - margin);
479 | }
480 | }
481 |
482 | mIndicatorRect.left = (int) left;
483 | mIndicatorRect.right = (int) right;
484 | //for mIndicatorWidthEqualTitle
485 | if (mIndicatorStyle == STYLE_NORMAL && mIndicatorWidthEqualTitle) {
486 | mIndicatorRect.left = (int) (left + margin - 1);
487 | mIndicatorRect.right = (int) (right - margin - 1);
488 | }
489 |
490 | mTabRect.left = (int) left;
491 | mTabRect.right = (int) right;
492 |
493 | if (mIndicatorWidth < 0) { //indicatorWidth小于0时,原jpardogo's PagerSlidingTabStrip
494 |
495 | } else {//indicatorWidth大于0时,圆角矩形以及三角形
496 | float indicatorLeft = currentTabView.getLeft() + (currentTabView.getWidth() - mIndicatorWidth) / 2;
497 |
498 | if (this.mCurrentTab < mTabCount - 1) {
499 | View nextTab = mTabsContainer.getChildAt(this.mCurrentTab + 1);
500 | indicatorLeft = indicatorLeft + mCurrentPositionOffset * (currentTabView.getWidth() / 2 + nextTab.getWidth() / 2);
501 | }
502 |
503 | mIndicatorRect.left = (int) indicatorLeft;
504 | mIndicatorRect.right = (int) (mIndicatorRect.left + mIndicatorWidth);
505 | }
506 | }
507 |
508 | @Override
509 | protected void onDraw(Canvas canvas) {
510 | super.onDraw(canvas);
511 |
512 | if (isInEditMode() || mTabCount <= 0) {
513 | return;
514 | }
515 |
516 | int height = getHeight();
517 | int paddingLeft = getPaddingLeft();
518 | // draw divider
519 | if (mDividerWidth > 0) {
520 | mDividerPaint.setStrokeWidth(mDividerWidth);
521 | mDividerPaint.setColor(mDividerColor);
522 | for (int i = 0; i < mTabCount - 1; i++) {
523 | View tab = mTabsContainer.getChildAt(i);
524 | canvas.drawLine(paddingLeft + tab.getRight(), mDividerPadding, paddingLeft + tab.getRight(), height - mDividerPadding, mDividerPaint);
525 | }
526 | }
527 |
528 | // draw underline
529 | if (mUnderlineHeight > 0) {
530 | mRectPaint.setColor(mUnderlineColor);
531 | if (mUnderlineGravity == Gravity.BOTTOM) {
532 | canvas.drawRect(paddingLeft, height - mUnderlineHeight, mTabsContainer.getWidth() + paddingLeft, height, mRectPaint);
533 | } else {
534 | canvas.drawRect(paddingLeft, 0, mTabsContainer.getWidth() + paddingLeft, mUnderlineHeight, mRectPaint);
535 | }
536 | }
537 |
538 | //draw indicator line
539 |
540 | calcIndicatorRect();
541 | if (mIndicatorStyle == STYLE_TRIANGLE) {
542 | if (mIndicatorHeight > 0) {
543 | mTrianglePaint.setColor(mIndicatorColor);
544 | mTrianglePath.reset();
545 | mTrianglePath.moveTo(paddingLeft + mIndicatorRect.left, height);
546 | mTrianglePath.lineTo(paddingLeft + mIndicatorRect.left / 2 + mIndicatorRect.right / 2, height - mIndicatorHeight);
547 | mTrianglePath.lineTo(paddingLeft + mIndicatorRect.right, height);
548 | mTrianglePath.close();
549 | canvas.drawPath(mTrianglePath, mTrianglePaint);
550 | }
551 | } else if (mIndicatorStyle == STYLE_BLOCK) {
552 | if (mIndicatorHeight < 0) {
553 | mIndicatorHeight = height - mIndicatorMarginTop - mIndicatorMarginBottom;
554 | } else {
555 |
556 | }
557 |
558 | if (mIndicatorHeight > 0) {
559 | if (mIndicatorCornerRadius < 0 || mIndicatorCornerRadius > mIndicatorHeight / 2) {
560 | mIndicatorCornerRadius = mIndicatorHeight / 2;
561 | }
562 |
563 | mIndicatorDrawable.setColor(mIndicatorColor);
564 | mIndicatorDrawable.setBounds(paddingLeft + (int) mIndicatorMarginLeft + mIndicatorRect.left,
565 | (int) mIndicatorMarginTop, (int) (paddingLeft + mIndicatorRect.right - mIndicatorMarginRight),
566 | (int) (mIndicatorMarginTop + mIndicatorHeight));
567 | mIndicatorDrawable.setCornerRadius(mIndicatorCornerRadius);
568 | mIndicatorDrawable.draw(canvas);
569 | }
570 | } else {
571 | /* mRectPaint.setColor(mIndicatorColor);
572 | calcIndicatorRect();
573 | canvas.drawRect(getPaddingLeft() + mIndicatorRect.left, getScreenHeight() - mIndicatorHeight,
574 | mIndicatorRect.right + getPaddingLeft(), getScreenHeight(), mRectPaint);*/
575 |
576 | if (mIndicatorHeight > 0) {
577 | mIndicatorDrawable.setColor(mIndicatorColor);
578 |
579 | if (mIndicatorGravity == Gravity.BOTTOM) {
580 | mIndicatorDrawable.setBounds(paddingLeft + (int) mIndicatorMarginLeft + mIndicatorRect.left,
581 | height - (int) mIndicatorHeight - (int) mIndicatorMarginBottom,
582 | paddingLeft + mIndicatorRect.right - (int) mIndicatorMarginRight,
583 | height - (int) mIndicatorMarginBottom);
584 | } else {
585 | mIndicatorDrawable.setBounds(paddingLeft + (int) mIndicatorMarginLeft + mIndicatorRect.left,
586 | (int) mIndicatorMarginTop,
587 | paddingLeft + mIndicatorRect.right - (int) mIndicatorMarginRight,
588 | (int) mIndicatorHeight + (int) mIndicatorMarginTop);
589 | }
590 | mIndicatorDrawable.setCornerRadius(mIndicatorCornerRadius);
591 | mIndicatorDrawable.draw(canvas);
592 | }
593 | }
594 | }
595 |
596 | //setter and getter
597 | public void setCurrentTab(int currentTab) {
598 | this.mCurrentTab = currentTab;
599 | mViewPager.setCurrentItem(currentTab);
600 |
601 | }
602 |
603 | public void setCurrentTab(int currentTab, boolean smoothScroll) {
604 | this.mCurrentTab = currentTab;
605 | mViewPager.setCurrentItem(currentTab, smoothScroll);
606 | }
607 |
608 | public void setIndicatorStyle(int indicatorStyle) {
609 | this.mIndicatorStyle = indicatorStyle;
610 | invalidate();
611 | }
612 |
613 | public void setTabPadding(float tabPadding) {
614 | this.mTabPadding = dp2px(tabPadding);
615 | updateTabStyles();
616 | }
617 |
618 | public void setTabSpaceEqual(boolean tabSpaceEqual) {
619 | this.mTabSpaceEqual = tabSpaceEqual;
620 | updateTabStyles();
621 | }
622 |
623 | public void setTabWidth(float tabWidth) {
624 | this.mTabWidth = dp2px(tabWidth);
625 | updateTabStyles();
626 | }
627 |
628 | public void setIndicatorColor(int indicatorColor) {
629 | this.mIndicatorColor = indicatorColor;
630 | invalidate();
631 | }
632 |
633 | public void setIndicatorHeight(float indicatorHeight) {
634 | this.mIndicatorHeight = dp2px(indicatorHeight);
635 | invalidate();
636 | }
637 |
638 | public void setIndicatorWidth(float indicatorWidth) {
639 | this.mIndicatorWidth = dp2px(indicatorWidth);
640 | invalidate();
641 | }
642 |
643 | public void setIndicatorCornerRadius(float indicatorCornerRadius) {
644 | this.mIndicatorCornerRadius = dp2px(indicatorCornerRadius);
645 | invalidate();
646 | }
647 |
648 | public void setIndicatorGravity(int indicatorGravity) {
649 | this.mIndicatorGravity = indicatorGravity;
650 | invalidate();
651 | }
652 |
653 | public void setIndicatorMargin(float indicatorMarginLeft, float indicatorMarginTop,
654 | float indicatorMarginRight, float indicatorMarginBottom) {
655 | this.mIndicatorMarginLeft = dp2px(indicatorMarginLeft);
656 | this.mIndicatorMarginTop = dp2px(indicatorMarginTop);
657 | this.mIndicatorMarginRight = dp2px(indicatorMarginRight);
658 | this.mIndicatorMarginBottom = dp2px(indicatorMarginBottom);
659 | invalidate();
660 | }
661 |
662 | public void setIndicatorWidthEqualTitle(boolean indicatorWidthEqualTitle) {
663 | this.mIndicatorWidthEqualTitle = indicatorWidthEqualTitle;
664 | invalidate();
665 | }
666 |
667 | public void setUnderlineColor(int underlineColor) {
668 | this.mUnderlineColor = underlineColor;
669 | invalidate();
670 | }
671 |
672 | public void setUnderlineHeight(float underlineHeight) {
673 | this.mUnderlineHeight = dp2px(underlineHeight);
674 | invalidate();
675 | }
676 |
677 | public void setUnderlineGravity(int underlineGravity) {
678 | this.mUnderlineGravity = underlineGravity;
679 | invalidate();
680 | }
681 |
682 | public void setDividerColor(int dividerColor) {
683 | this.mDividerColor = dividerColor;
684 | invalidate();
685 | }
686 |
687 | public void setDividerWidth(float dividerWidth) {
688 | this.mDividerWidth = dp2px(dividerWidth);
689 | invalidate();
690 | }
691 |
692 | public void setDividerPadding(float dividerPadding) {
693 | this.mDividerPadding = dp2px(dividerPadding);
694 | invalidate();
695 | }
696 |
697 | public void setTextsize(float textsize) {
698 | this.mTextsize = sp2px(textsize);
699 | updateTabStyles();
700 | }
701 |
702 | public void setTextSelectColor(int textSelectColor) {
703 | this.mTextSelectColor = textSelectColor;
704 | updateTabStyles();
705 | }
706 |
707 | public void setTextUnselectColor(int textUnselectColor) {
708 | this.mTextUnselectColor = textUnselectColor;
709 | updateTabStyles();
710 | }
711 |
712 | public void setTextBold(int textBold) {
713 | this.mTextBold = textBold;
714 | updateTabStyles();
715 | }
716 |
717 | public void setTextAllCaps(boolean textAllCaps) {
718 | this.mTextAllCaps = textAllCaps;
719 | updateTabStyles();
720 | }
721 |
722 | public void setSnapOnTabClick(boolean snapOnTabClick) {
723 | mSnapOnTabClick = snapOnTabClick;
724 | }
725 |
726 |
727 | public int getTabCount() {
728 | return mTabCount;
729 | }
730 |
731 | public int getCurrentTab() {
732 | return mCurrentTab;
733 | }
734 |
735 | public int getIndicatorStyle() {
736 | return mIndicatorStyle;
737 | }
738 |
739 | public float getTabPadding() {
740 | return mTabPadding;
741 | }
742 |
743 | public boolean isTabSpaceEqual() {
744 | return mTabSpaceEqual;
745 | }
746 |
747 | public float getTabWidth() {
748 | return mTabWidth;
749 | }
750 |
751 | public int getIndicatorColor() {
752 | return mIndicatorColor;
753 | }
754 |
755 | public float getIndicatorHeight() {
756 | return mIndicatorHeight;
757 | }
758 |
759 | public float getIndicatorWidth() {
760 | return mIndicatorWidth;
761 | }
762 |
763 | public float getIndicatorCornerRadius() {
764 | return mIndicatorCornerRadius;
765 | }
766 |
767 | public float getIndicatorMarginLeft() {
768 | return mIndicatorMarginLeft;
769 | }
770 |
771 | public float getIndicatorMarginTop() {
772 | return mIndicatorMarginTop;
773 | }
774 |
775 | public float getIndicatorMarginRight() {
776 | return mIndicatorMarginRight;
777 | }
778 |
779 | public float getIndicatorMarginBottom() {
780 | return mIndicatorMarginBottom;
781 | }
782 |
783 | public int getUnderlineColor() {
784 | return mUnderlineColor;
785 | }
786 |
787 | public float getUnderlineHeight() {
788 | return mUnderlineHeight;
789 | }
790 |
791 | public int getDividerColor() {
792 | return mDividerColor;
793 | }
794 |
795 | public float getDividerWidth() {
796 | return mDividerWidth;
797 | }
798 |
799 | public float getDividerPadding() {
800 | return mDividerPadding;
801 | }
802 |
803 | public float getTextsize() {
804 | return mTextsize;
805 | }
806 |
807 | public int getTextSelectColor() {
808 | return mTextSelectColor;
809 | }
810 |
811 | public int getTextUnselectColor() {
812 | return mTextUnselectColor;
813 | }
814 |
815 | public int getTextBold() {
816 | return mTextBold;
817 | }
818 |
819 | public boolean isTextAllCaps() {
820 | return mTextAllCaps;
821 | }
822 |
823 | public TextView getTitleView(int tab) {
824 | View tabView = mTabsContainer.getChildAt(tab);
825 | TextView tv_tab_title = (TextView) tabView.findViewById(R.id.tv_tab_title);
826 | return tv_tab_title;
827 | }
828 |
829 | //setter and getter
830 |
831 | // show MsgTipView
832 | private Paint mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
833 | private SparseArray mInitSetMap = new SparseArray<>();
834 |
835 | private OnTabSelectListener mListener;
836 |
837 | public interface OnTabSelectListener {
838 | void onTabSelect(int position);
839 |
840 | void onTabReselect(int position);
841 | }
842 |
843 | public void setOnTabSelectListener(OnTabSelectListener listener) {
844 | this.mListener = listener;
845 | }
846 |
847 | class InnerPagerAdapter extends FragmentPagerAdapter {
848 | private ArrayList fragments = new ArrayList<>();
849 | private String[] titles;
850 |
851 | public InnerPagerAdapter(FragmentManager fm, ArrayList fragments, String[] titles) {
852 | super(fm);
853 | this.fragments = fragments;
854 | this.titles = titles;
855 | }
856 |
857 | @Override
858 | public int getCount() {
859 | return fragments.size();
860 | }
861 |
862 | @Override
863 | public CharSequence getPageTitle(int position) {
864 | return titles[position];
865 | }
866 |
867 | @Override
868 | public Fragment getItem(int position) {
869 | return fragments.get(position);
870 | }
871 |
872 | @Override
873 | public void destroyItem(ViewGroup container, int position, Object object) {
874 | // 覆写destroyItem并且空实现,这样每个Fragment中的视图就不会被销毁
875 | // super.destroyItem(container, position, object);
876 | }
877 |
878 | @Override
879 | public int getItemPosition(Object object) {
880 | return PagerAdapter.POSITION_NONE;
881 | }
882 | }
883 |
884 | @Override
885 | protected Parcelable onSaveInstanceState() {
886 | Bundle bundle = new Bundle();
887 | bundle.putParcelable("instanceState", super.onSaveInstanceState());
888 | bundle.putInt("mCurrentTab", mCurrentTab);
889 | return bundle;
890 | }
891 |
892 | @Override
893 | protected void onRestoreInstanceState(Parcelable state) {
894 | if (state instanceof Bundle) {
895 | Bundle bundle = (Bundle) state;
896 | mCurrentTab = bundle.getInt("mCurrentTab");
897 | state = bundle.getParcelable("instanceState");
898 | if (mCurrentTab != 0 && mTabsContainer.getChildCount() > 0) {
899 | updateTabSelection(mCurrentTab);
900 | scrollToCurrentTab();
901 | }
902 | }
903 | super.onRestoreInstanceState(state);
904 | }
905 |
906 | protected int dp2px(float dp) {
907 | final float scale = mContext.getResources().getDisplayMetrics().density;
908 | return (int) (dp * scale + 0.5f);
909 | }
910 |
911 | protected int sp2px(float sp) {
912 | final float scale = this.mContext.getResources().getDisplayMetrics().scaledDensity;
913 | return (int) (sp * scale + 0.5f);
914 | }
915 | }
--------------------------------------------------------------------------------