├── .gitignore ├── .idea ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── gradle.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── handmall │ │ └── tabbar │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── handmall │ │ │ └── tabbar │ │ │ ├── activity │ │ │ ├── MainActivity.java │ │ │ └── PullToRefreshActivity.java │ │ │ ├── fragment │ │ │ ├── BaseRefreshFragment.java │ │ │ ├── CityFragment.java │ │ │ ├── HomeFragment.java │ │ │ ├── ListViewFragment.java │ │ │ ├── MessageFragment.java │ │ │ ├── PersonFragment.java │ │ │ └── RecyclerViewFragment.java │ │ │ ├── util │ │ │ └── ThemeUtils.java │ │ │ └── widget │ │ │ ├── MainNavigateTabBar.java │ │ │ └── PullToRefresh │ │ │ ├── PullToRefreshView.java │ │ │ ├── refresh_view │ │ │ ├── BaseRefreshView.java │ │ │ └── SunRefreshView.java │ │ │ └── util │ │ │ ├── Logger.java │ │ │ └── Utils.java │ └── res │ │ ├── drawable-hdpi │ │ ├── buildings.png │ │ ├── icon_1.png │ │ ├── icon_2.png │ │ ├── icon_3.png │ │ ├── lib_icon.png │ │ ├── sky.png │ │ └── sun.png │ │ ├── drawable-mdpi │ │ ├── buildings.png │ │ ├── icon_1.png │ │ ├── icon_2.png │ │ ├── icon_3.png │ │ ├── lib_icon.png │ │ ├── sky.png │ │ └── sun.png │ │ ├── drawable-xhdpi │ │ ├── buildings.png │ │ ├── icon_1.png │ │ ├── icon_2.png │ │ ├── icon_3.png │ │ ├── lib_icon.png │ │ ├── sky.png │ │ └── sun.png │ │ ├── drawable-xxhdpi │ │ ├── buildings.png │ │ ├── icon_1.png │ │ ├── icon_2.png │ │ ├── icon_3.png │ │ ├── lib_icon.png │ │ ├── sky.png │ │ └── sun.png │ │ ├── drawable-xxxhdpi │ │ ├── buildings.png │ │ ├── icon_1.png │ │ ├── icon_2.png │ │ ├── icon_3.png │ │ ├── lib_icon.png │ │ ├── sky.png │ │ └── sun.png │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── activity_pull_to_refresh.xml │ │ ├── comui_tab_view.xml │ │ ├── fragment_city.xml │ │ ├── fragment_home.xml │ │ ├── fragment_list_view.xml │ │ ├── fragment_message.xml │ │ ├── fragment_person.xml │ │ ├── fragment_recycler_view.xml │ │ └── list_item.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ ├── comui_bar_top_shadow.png │ │ ├── comui_tab_city.png │ │ ├── comui_tab_city_selected.png │ │ ├── comui_tab_find.png │ │ ├── comui_tab_find_selected.png │ │ ├── comui_tab_home.png │ │ ├── comui_tab_home_selected.png │ │ ├── comui_tab_message.png │ │ ├── comui_tab_message_selected.png │ │ ├── comui_tab_person.png │ │ ├── comui_tab_person_selected.png │ │ ├── comui_tab_post.png │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-w820dp │ │ ├── attr.xml │ │ └── dimens.xml │ │ └── values │ │ ├── attrs.xml │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── handmall │ └── tabbar │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── screenshots └── 1.png └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | 47 | 48 | 49 | 50 | 1.8 51 | 52 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tabbar 2 | ![image](https://github.com/teulu/tabbar/blob/master/screenshots/1.png) 3 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.3" 6 | defaultConfig { 7 | applicationId "com.handmall.tabbar" 8 | minSdkVersion 19 9 | targetSdkVersion 23 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 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 25 | exclude group: 'com.android.support', module: 'support-annotations' 26 | }) 27 | compile('com.android.support:design:23.1.1') { 28 | force = true; 29 | } 30 | compile('com.android.support:recyclerview-v7:23.1.1') { 31 | force = true; 32 | } 33 | compile('com.android.support:appcompat-v7:23.1.1') { 34 | force = true; 35 | } 36 | testCompile 'junit:junit:4.12' 37 | } 38 | -------------------------------------------------------------------------------- /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 /Users/java/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/handmall/tabbar/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.handmall.tabbar; 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.handmall.tabbar", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/java/com/handmall/tabbar/activity/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.handmall.tabbar.activity; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.view.View; 6 | import android.widget.Toast; 7 | 8 | import com.handmall.tabbar.R; 9 | import com.handmall.tabbar.fragment.CityFragment; 10 | import com.handmall.tabbar.fragment.HomeFragment; 11 | import com.handmall.tabbar.fragment.ListViewFragment; 12 | import com.handmall.tabbar.fragment.MessageFragment; 13 | import com.handmall.tabbar.fragment.PersonFragment; 14 | import com.handmall.tabbar.widget.MainNavigateTabBar; 15 | 16 | 17 | public class MainActivity extends AppCompatActivity { 18 | 19 | private static final String TAG_PAGE_HOME = "首页"; 20 | private static final String TAG_PAGE_CITY = "同城"; 21 | private static final String TAG_PAGE_PUBLISH = "发布"; 22 | private static final String TAG_PAGE_MESSAGE = "消息"; 23 | private static final String TAG_PAGE_PERSON = "我的"; 24 | 25 | 26 | private MainNavigateTabBar mNavigateTabBar; 27 | 28 | @Override 29 | protected void onCreate(Bundle savedInstanceState) { 30 | super.onCreate(savedInstanceState); 31 | setContentView(R.layout.activity_main); 32 | 33 | mNavigateTabBar = (MainNavigateTabBar) findViewById(R.id.mainTabBar); 34 | 35 | mNavigateTabBar.onRestoreInstanceState(savedInstanceState); 36 | 37 | mNavigateTabBar.addTab(ListViewFragment.class, new MainNavigateTabBar.TabParam(R.mipmap.comui_tab_home, R.mipmap.comui_tab_home_selected, TAG_PAGE_HOME)); 38 | mNavigateTabBar.addTab(CityFragment.class, new MainNavigateTabBar.TabParam(R.mipmap.comui_tab_city, R.mipmap.comui_tab_city_selected, TAG_PAGE_CITY)); 39 | mNavigateTabBar.addTab(null, new MainNavigateTabBar.TabParam(0, 0, TAG_PAGE_PUBLISH)); 40 | mNavigateTabBar.addTab(MessageFragment.class, new MainNavigateTabBar.TabParam(R.mipmap.comui_tab_message, R.mipmap.comui_tab_message_selected, TAG_PAGE_MESSAGE)); 41 | mNavigateTabBar.addTab(PersonFragment.class, new MainNavigateTabBar.TabParam(R.mipmap.comui_tab_person, R.mipmap.comui_tab_person_selected, TAG_PAGE_PERSON)); 42 | } 43 | 44 | 45 | @Override 46 | protected void onSaveInstanceState(Bundle outState) { 47 | super.onSaveInstanceState(outState); 48 | mNavigateTabBar.onSaveInstanceState(outState); 49 | } 50 | 51 | 52 | public void onClickPublish(View v) { 53 | Toast.makeText(this, "发布", Toast.LENGTH_LONG).show(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /app/src/main/java/com/handmall/tabbar/activity/PullToRefreshActivity.java: -------------------------------------------------------------------------------- 1 | package com.handmall.tabbar.activity; 2 | 3 | import android.os.Bundle; 4 | import android.support.design.widget.TabLayout; 5 | import android.app.Fragment; 6 | import android.support.v4.app.FragmentManager; 7 | import android.support.v4.app.FragmentPagerAdapter; 8 | import android.support.v4.view.ViewPager; 9 | import android.support.v7.app.AppCompatActivity; 10 | import android.support.v7.widget.Toolbar; 11 | 12 | import com.handmall.tabbar.R; 13 | import com.handmall.tabbar.fragment.ListViewFragment; 14 | import com.handmall.tabbar.fragment.RecyclerViewFragment; 15 | 16 | /** 17 | * Created by Oleksii Shliama. 18 | */ 19 | public class PullToRefreshActivity extends AppCompatActivity { 20 | 21 | @Override 22 | protected void onCreate(Bundle savedInstanceState) { 23 | super.onCreate(savedInstanceState); 24 | setContentView(R.layout.activity_pull_to_refresh); 25 | 26 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 27 | TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout); 28 | ViewPager viewPager = (ViewPager) findViewById(R.id.pager); 29 | 30 | if (toolbar != null) { 31 | setSupportActionBar(toolbar); 32 | } 33 | 34 | viewPager.setAdapter(new SectionPagerAdapter(getSupportFragmentManager())); 35 | tabLayout.setupWithViewPager(viewPager); 36 | } 37 | 38 | public class SectionPagerAdapter extends FragmentPagerAdapter { 39 | 40 | public SectionPagerAdapter(FragmentManager fm) { 41 | super(fm); 42 | } 43 | 44 | @Override 45 | public android.support.v4.app.Fragment getItem(int position) { 46 | return null; 47 | } 48 | 49 | // @Override 50 | // public Fragment getItem(int position) { 51 | // switch (position) { 52 | // case 0: 53 | // return new ListViewFragment(); 54 | // case 1: 55 | // default: 56 | // return new RecyclerViewFragment(); 57 | // } 58 | // } 59 | 60 | @Override 61 | public int getCount() { 62 | return 2; 63 | } 64 | 65 | @Override 66 | public CharSequence getPageTitle(int position) { 67 | switch (position) { 68 | case 0: 69 | return "ListView"; 70 | case 1: 71 | default: 72 | return "RecyclerView"; 73 | } 74 | } 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /app/src/main/java/com/handmall/tabbar/fragment/BaseRefreshFragment.java: -------------------------------------------------------------------------------- 1 | package com.handmall.tabbar.fragment; 2 | 3 | import android.os.Bundle; 4 | import android.app.Fragment; 5 | 6 | import com.handmall.tabbar.R; 7 | 8 | import java.util.ArrayList; 9 | import java.util.HashMap; 10 | import java.util.List; 11 | import java.util.Map; 12 | 13 | /** 14 | * Created by Oleksii Shliama. 15 | */ 16 | public class BaseRefreshFragment extends Fragment { 17 | 18 | public static final int REFRESH_DELAY = 2000; 19 | 20 | public static final String KEY_ICON = "icon"; 21 | public static final String KEY_COLOR = "color"; 22 | 23 | protected List> mSampleList; 24 | 25 | @Override 26 | public void onCreate(Bundle savedInstanceState) { 27 | super.onCreate(savedInstanceState); 28 | 29 | Map map; 30 | mSampleList = new ArrayList<>(); 31 | 32 | int[] icons = { 33 | R.drawable.icon_1, 34 | R.drawable.icon_2, 35 | R.drawable.icon_3}; 36 | 37 | int[] colors = { 38 | R.color.saffron, 39 | R.color.eggplant, 40 | R.color.sienna}; 41 | 42 | for (int i = 0; i < icons.length; i++) { 43 | map = new HashMap<>(); 44 | map.put(KEY_ICON, icons[i]); 45 | map.put(KEY_COLOR, colors[i]); 46 | mSampleList.add(map); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/java/com/handmall/tabbar/fragment/CityFragment.java: -------------------------------------------------------------------------------- 1 | package com.handmall.tabbar.fragment; 2 | 3 | import android.app.Fragment; 4 | import android.os.Bundle; 5 | import android.support.annotation.Nullable; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | 10 | import com.handmall.tabbar.R; 11 | 12 | /** 13 | * User:Shine 14 | * Date:2015-10-20 15 | * Description: 16 | */ 17 | public class CityFragment extends Fragment { 18 | 19 | @Nullable 20 | @Override 21 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 22 | return inflater.inflate(R.layout.fragment_city, container, false); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/handmall/tabbar/fragment/HomeFragment.java: -------------------------------------------------------------------------------- 1 | package com.handmall.tabbar.fragment; 2 | 3 | import android.app.Fragment; 4 | import android.os.Bundle; 5 | import android.support.annotation.Nullable; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | 10 | import com.handmall.tabbar.R; 11 | 12 | /** 13 | * User:Shine 14 | * Date:2015-10-20 15 | * Description: 16 | */ 17 | public class HomeFragment extends Fragment { 18 | 19 | @Nullable 20 | @Override 21 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 22 | return inflater.inflate(R.layout.fragment_home, container, false); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/handmall/tabbar/fragment/ListViewFragment.java: -------------------------------------------------------------------------------- 1 | package com.handmall.tabbar.fragment; 2 | 3 | import android.content.Context; 4 | import android.os.Bundle; 5 | import android.support.annotation.NonNull; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.ArrayAdapter; 10 | import android.widget.ImageView; 11 | import android.widget.ListView; 12 | 13 | import com.handmall.tabbar.R; 14 | import com.handmall.tabbar.widget.PullToRefresh.PullToRefreshView; 15 | 16 | import java.util.List; 17 | import java.util.Map; 18 | 19 | /** 20 | * Created by Oleksii Shliama. 21 | */ 22 | public class ListViewFragment extends BaseRefreshFragment { 23 | 24 | private PullToRefreshView mPullToRefreshView; 25 | 26 | @Override 27 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 28 | ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.fragment_list_view, container, false); 29 | 30 | ListView listView = (ListView) rootView.findViewById(R.id.list_view); 31 | listView.setAdapter(new SampleAdapter(getActivity(), R.layout.list_item, mSampleList)); 32 | 33 | mPullToRefreshView = (PullToRefreshView) rootView.findViewById(R.id.pull_to_refresh); 34 | mPullToRefreshView.setOnRefreshListener(new PullToRefreshView.OnRefreshListener() { 35 | @Override 36 | public void onRefresh() { 37 | mPullToRefreshView.postDelayed(new Runnable() { 38 | @Override 39 | public void run() { 40 | mPullToRefreshView.setRefreshing(false); 41 | } 42 | }, REFRESH_DELAY); 43 | } 44 | 45 | @Override 46 | public void onFinish() { 47 | 48 | } 49 | 50 | @Override 51 | public void ondragDistanceChange(float distance, float percent, float offset) { 52 | 53 | } 54 | }); 55 | 56 | return rootView; 57 | } 58 | 59 | class SampleAdapter extends ArrayAdapter> { 60 | 61 | public static final String KEY_ICON = "icon"; 62 | public static final String KEY_COLOR = "color"; 63 | 64 | private final LayoutInflater mInflater; 65 | private final List> mData; 66 | 67 | public SampleAdapter(Context context, int layoutResourceId, List> data) { 68 | super(context, layoutResourceId, data); 69 | mData = data; 70 | mInflater = LayoutInflater.from(context); 71 | } 72 | 73 | @Override 74 | public View getView(final int position, View convertView, @NonNull ViewGroup parent) { 75 | final ViewHolder viewHolder; 76 | if (convertView == null) { 77 | viewHolder = new ViewHolder(); 78 | convertView = mInflater.inflate(R.layout.list_item, parent, false); 79 | viewHolder.imageViewIcon = (ImageView) convertView.findViewById(R.id.image_view_icon); 80 | convertView.setTag(viewHolder); 81 | } else { 82 | viewHolder = (ViewHolder) convertView.getTag(); 83 | } 84 | 85 | viewHolder.imageViewIcon.setImageResource(mData.get(position).get(KEY_ICON)); 86 | convertView.setBackgroundResource(mData.get(position).get(KEY_COLOR)); 87 | 88 | return convertView; 89 | } 90 | 91 | class ViewHolder { 92 | ImageView imageViewIcon; 93 | } 94 | 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /app/src/main/java/com/handmall/tabbar/fragment/MessageFragment.java: -------------------------------------------------------------------------------- 1 | package com.handmall.tabbar.fragment; 2 | 3 | import android.app.Fragment; 4 | import android.os.Bundle; 5 | import android.support.annotation.Nullable; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | 10 | import com.handmall.tabbar.R; 11 | 12 | /** 13 | * User:Shine 14 | * Date:2015-10-20 15 | * Description: 16 | */ 17 | public class MessageFragment extends Fragment { 18 | 19 | @Nullable 20 | @Override 21 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 22 | return inflater.inflate(R.layout.fragment_message, container, false); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/handmall/tabbar/fragment/PersonFragment.java: -------------------------------------------------------------------------------- 1 | package com.handmall.tabbar.fragment; 2 | 3 | import android.app.Fragment; 4 | import android.os.Bundle; 5 | import android.support.annotation.Nullable; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | 10 | import com.handmall.tabbar.R; 11 | 12 | /** 13 | * User:Shine 14 | * Date:2015-10-20 15 | * Description: 16 | */ 17 | public class PersonFragment extends Fragment { 18 | 19 | @Nullable 20 | @Override 21 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 22 | return inflater.inflate(R.layout.fragment_person, container, false); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/handmall/tabbar/fragment/RecyclerViewFragment.java: -------------------------------------------------------------------------------- 1 | package com.handmall.tabbar.fragment; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.widget.LinearLayoutManager; 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 | 11 | import com.handmall.tabbar.R; 12 | import com.handmall.tabbar.widget.PullToRefresh.PullToRefreshView; 13 | 14 | import java.util.Map; 15 | 16 | /** 17 | * Created by Oleksii Shliama. 18 | */ 19 | public class RecyclerViewFragment extends BaseRefreshFragment { 20 | 21 | private PullToRefreshView mPullToRefreshView; 22 | 23 | @Override 24 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 25 | ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.fragment_recycler_view, container, false); 26 | 27 | RecyclerView recyclerView = (RecyclerView) rootView.findViewById(R.id.recycler_view); 28 | recyclerView.setLayoutManager(new LinearLayoutManager(getActivity())); 29 | 30 | recyclerView.setAdapter(new SampleAdapter()); 31 | 32 | mPullToRefreshView = (PullToRefreshView) rootView.findViewById(R.id.pull_to_refresh); 33 | mPullToRefreshView.setOnRefreshListener(new PullToRefreshView.OnRefreshListener() { 34 | @Override 35 | public void onRefresh() { 36 | mPullToRefreshView.postDelayed(new Runnable() { 37 | @Override 38 | public void run() { 39 | mPullToRefreshView.setRefreshing(false); 40 | } 41 | }, REFRESH_DELAY); 42 | } 43 | 44 | @Override 45 | public void onFinish() { 46 | 47 | 48 | } 49 | 50 | @Override 51 | public void ondragDistanceChange(float distance, float percent, float offset) { 52 | 53 | } 54 | }); 55 | 56 | return rootView; 57 | } 58 | 59 | private class SampleAdapter extends RecyclerView.Adapter { 60 | 61 | @Override 62 | public SampleHolder onCreateViewHolder(ViewGroup parent, int pos) { 63 | View view = LayoutInflater.from(parent.getContext()) 64 | .inflate(R.layout.list_item, parent, false); 65 | return new SampleHolder(view); 66 | } 67 | 68 | @Override 69 | public void onBindViewHolder(SampleHolder holder, int pos) { 70 | Map data = mSampleList.get(pos); 71 | holder.bindData(data); 72 | } 73 | 74 | @Override 75 | public int getItemCount() { 76 | return mSampleList.size(); 77 | } 78 | } 79 | 80 | private class SampleHolder extends RecyclerView.ViewHolder { 81 | 82 | private View mRootView; 83 | private ImageView mImageViewIcon; 84 | 85 | private Map mData; 86 | 87 | public SampleHolder(View itemView) { 88 | super(itemView); 89 | 90 | mRootView = itemView; 91 | mImageViewIcon = (ImageView) itemView.findViewById(R.id.image_view_icon); 92 | } 93 | 94 | public void bindData(Map data) { 95 | mData = data; 96 | 97 | mRootView.setBackgroundResource(mData.get(KEY_COLOR)); 98 | mImageViewIcon.setImageResource(mData.get(KEY_ICON)); 99 | } 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /app/src/main/java/com/handmall/tabbar/util/ThemeUtils.java: -------------------------------------------------------------------------------- 1 | package com.handmall.tabbar.util; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | 6 | import com.handmall.tabbar.R; 7 | 8 | /** 9 | * User:Shine 10 | * Date:2015-11-20 11 | * Description: 12 | */ 13 | public class ThemeUtils { 14 | 15 | private static final int[] APPCOMPAT_CHECK_ATTRS = {R.attr.colorPrimary}; 16 | 17 | public static void checkAppCompatTheme(Context context) { 18 | TypedArray a = context.obtainStyledAttributes(APPCOMPAT_CHECK_ATTRS); 19 | final boolean failed = !a.hasValue(0); 20 | if (a != null) { 21 | a.recycle(); 22 | } 23 | if (failed) { 24 | throw new IllegalArgumentException("You need to use a Theme.AppCompat theme " 25 | + "(or descendant) with the design library."); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/handmall/tabbar/widget/MainNavigateTabBar.java: -------------------------------------------------------------------------------- 1 | package com.handmall.tabbar.widget; 2 | 3 | import android.app.Fragment; 4 | import android.app.FragmentTransaction; 5 | import android.content.Context; 6 | import android.content.res.ColorStateList; 7 | import android.content.res.TypedArray; 8 | import android.os.Bundle; 9 | import android.support.v4.app.FragmentActivity; 10 | import android.text.TextUtils; 11 | import android.util.AttributeSet; 12 | import android.util.TypedValue; 13 | import android.view.LayoutInflater; 14 | import android.view.View; 15 | import android.view.ViewGroup; 16 | import android.widget.ImageView; 17 | import android.widget.LinearLayout; 18 | import android.widget.TextView; 19 | 20 | import com.handmall.tabbar.R; 21 | import com.handmall.tabbar.util.ThemeUtils; 22 | 23 | import java.util.ArrayList; 24 | import java.util.List; 25 | 26 | /** 27 | * User:Shine 28 | * Date:2015-10-29 29 | * Description: 30 | */ 31 | public class MainNavigateTabBar extends LinearLayout implements View.OnClickListener { 32 | 33 | private static final String KEY_CURRENT_TAG = "com.startsmake.template.currentTag"; 34 | 35 | private List mViewHolderList; 36 | private OnTabSelectedListener mTabSelectListener; 37 | private FragmentActivity mFragmentActivity; 38 | private String mCurrentTag; 39 | private String mRestoreTag; 40 | /*主内容显示区域View的id*/ 41 | private int mMainContentLayoutId; 42 | /*选中的Tab文字颜色*/ 43 | private ColorStateList mSelectedTextColor; 44 | /*正常的Tab文字颜色*/ 45 | private ColorStateList mNormalTextColor; 46 | /*Tab文字的颜色*/ 47 | private float mTabTextSize; 48 | /*默认选中的tab index*/ 49 | private int mDefaultSelectedTab = 0; 50 | 51 | private int mCurrentSelectedTab; 52 | 53 | public MainNavigateTabBar(Context context) { 54 | this(context, null); 55 | } 56 | 57 | public MainNavigateTabBar(Context context, AttributeSet attrs) { 58 | this(context, attrs, 0); 59 | } 60 | 61 | public MainNavigateTabBar(Context context, AttributeSet attrs, int defStyleAttr) { 62 | super(context, attrs, defStyleAttr); 63 | 64 | TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.MainNavigateTabBar, 0, 0); 65 | 66 | ColorStateList tabTextColor = typedArray.getColorStateList(R.styleable.MainNavigateTabBar_navigateTabTextColor); 67 | ColorStateList selectedTabTextColor = typedArray.getColorStateList(R.styleable.MainNavigateTabBar_navigateTabSelectedTextColor); 68 | 69 | mTabTextSize = typedArray.getDimensionPixelSize(R.styleable.MainNavigateTabBar_navigateTabTextSize, 0); 70 | mMainContentLayoutId = typedArray.getResourceId(R.styleable.MainNavigateTabBar_containerId, 0); 71 | 72 | mNormalTextColor = (tabTextColor != null ? tabTextColor : context.getResources().getColorStateList(R.color.tab_text_normal)); 73 | 74 | 75 | if (selectedTabTextColor != null) { 76 | mSelectedTextColor = selectedTabTextColor; 77 | } else { 78 | ThemeUtils.checkAppCompatTheme(context); 79 | TypedValue typedValue = new TypedValue(); 80 | context.getTheme().resolveAttribute(R.attr.colorPrimary, typedValue, true); 81 | mSelectedTextColor = context.getResources().getColorStateList(typedValue.resourceId); 82 | } 83 | 84 | mViewHolderList = new ArrayList<>(); 85 | } 86 | 87 | 88 | public void addTab(Class frameLayoutClass, TabParam tabParam) { 89 | int defaultLayout = R.layout.comui_tab_view; 90 | // if (tabParam.tabViewResId > 0) { 91 | // defaultLayout = tabParam.tabViewResId; 92 | // } 93 | if (TextUtils.isEmpty(tabParam.title)) { 94 | tabParam.title = getContext().getString(tabParam.titleStringRes); 95 | } 96 | 97 | View view = LayoutInflater.from(getContext()).inflate(defaultLayout, null); 98 | view.setFocusable(true); 99 | 100 | ViewHolder holder = new ViewHolder(); 101 | 102 | holder.tabIndex = mViewHolderList.size(); 103 | 104 | holder.fragmentClass = frameLayoutClass; 105 | holder.tag = tabParam.title; 106 | holder.pageParam = tabParam; 107 | 108 | holder.tabIcon = (ImageView) view.findViewById(R.id.tab_icon); 109 | holder.tabTitle = ((TextView) view.findViewById(R.id.tab_title)); 110 | 111 | if (TextUtils.isEmpty(tabParam.title)) { 112 | holder.tabTitle.setVisibility(View.INVISIBLE); 113 | } else { 114 | holder.tabTitle.setText(tabParam.title); 115 | } 116 | 117 | if (mTabTextSize != 0) { 118 | holder.tabTitle.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTabTextSize); 119 | } 120 | if (mNormalTextColor != null) { 121 | holder.tabTitle.setTextColor(mNormalTextColor); 122 | } 123 | 124 | if (tabParam.backgroundColor > 0) { 125 | view.setBackgroundResource(tabParam.backgroundColor); 126 | } 127 | 128 | if (tabParam.iconResId > 0) { 129 | holder.tabIcon.setImageResource(tabParam.iconResId); 130 | } else { 131 | holder.tabIcon.setVisibility(View.INVISIBLE); 132 | } 133 | 134 | if (tabParam.iconResId > 0 && tabParam.iconSelectedResId > 0) { 135 | view.setTag(holder); 136 | view.setOnClickListener(this); 137 | mViewHolderList.add(holder); 138 | } 139 | 140 | addView(view, new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, 1.0F)); 141 | 142 | } 143 | 144 | @Override 145 | protected void onAttachedToWindow() { 146 | super.onAttachedToWindow(); 147 | if (mMainContentLayoutId == 0) { 148 | throw new RuntimeException("mFrameLayoutId Cannot be 0"); 149 | } 150 | if (mViewHolderList.size() == 0) { 151 | throw new RuntimeException("mViewHolderList.size Cannot be 0, Please call addTab()"); 152 | } 153 | if (!(getContext() instanceof FragmentActivity)) { 154 | throw new RuntimeException("parent activity must is extends FragmentActivity"); 155 | } 156 | mFragmentActivity = (FragmentActivity) getContext(); 157 | 158 | ViewHolder defaultHolder = null; 159 | 160 | hideAllFragment(); 161 | if (!TextUtils.isEmpty(mRestoreTag)) { 162 | for (ViewHolder holder : mViewHolderList) { 163 | if (TextUtils.equals(mRestoreTag, holder.tag)) { 164 | defaultHolder = holder; 165 | mRestoreTag = null; 166 | break; 167 | } 168 | } 169 | } else { 170 | defaultHolder = mViewHolderList.get(mDefaultSelectedTab); 171 | } 172 | 173 | showFragment(defaultHolder); 174 | } 175 | 176 | @Override 177 | public void onClick(View v) { 178 | Object object = v.getTag(); 179 | if (object != null && object instanceof ViewHolder) { 180 | ViewHolder holder = (ViewHolder) v.getTag(); 181 | showFragment(holder); 182 | if (mTabSelectListener != null) { 183 | mTabSelectListener.onTabSelected(holder); 184 | } 185 | } 186 | } 187 | 188 | /** 189 | * 显示 holder 对应的 fragment 190 | * 191 | * @param holder 192 | */ 193 | private void showFragment(ViewHolder holder) { 194 | FragmentTransaction transaction = mFragmentActivity.getFragmentManager().beginTransaction(); 195 | if (isFragmentShown(transaction, holder.tag)) { 196 | return; 197 | } 198 | setCurrSelectedTabByTag(holder.tag); 199 | 200 | Fragment fragment = mFragmentActivity.getFragmentManager().findFragmentByTag(holder.tag); 201 | if (fragment == null) { 202 | fragment = getFragmentInstance(holder.tag); 203 | transaction.add(mMainContentLayoutId, fragment, holder.tag); 204 | } else { 205 | transaction.show(fragment); 206 | } 207 | transaction.commit(); 208 | mCurrentSelectedTab = holder.tabIndex; 209 | } 210 | 211 | private boolean isFragmentShown(FragmentTransaction transaction, String newTag) { 212 | if (TextUtils.equals(newTag, mCurrentTag)) { 213 | return true; 214 | } 215 | 216 | if (TextUtils.isEmpty(mCurrentTag)) { 217 | return false; 218 | } 219 | 220 | Fragment fragment = mFragmentActivity.getFragmentManager().findFragmentByTag(mCurrentTag); 221 | if (fragment != null && !fragment.isHidden()) { 222 | transaction.hide(fragment); 223 | } 224 | 225 | return false; 226 | } 227 | 228 | /*设置当前选中tab的图片和文字颜色*/ 229 | private void setCurrSelectedTabByTag(String tag) { 230 | if (TextUtils.equals(mCurrentTag, tag)) { 231 | return; 232 | } 233 | for (ViewHolder holder : mViewHolderList) { 234 | if (TextUtils.equals(mCurrentTag, holder.tag)) { 235 | holder.tabIcon.setImageResource(holder.pageParam.iconResId); 236 | holder.tabTitle.setTextColor(mNormalTextColor); 237 | } else if (TextUtils.equals(tag, holder.tag)) { 238 | holder.tabIcon.setImageResource(holder.pageParam.iconSelectedResId); 239 | holder.tabTitle.setTextColor(mSelectedTextColor); 240 | } 241 | } 242 | mCurrentTag = tag; 243 | } 244 | 245 | 246 | private Fragment getFragmentInstance(String tag) { 247 | Fragment fragment = null; 248 | for (ViewHolder holder : mViewHolderList) { 249 | if (TextUtils.equals(tag, holder.tag)) { 250 | try { 251 | fragment = (Fragment) Class.forName(holder.fragmentClass.getName()).newInstance(); 252 | } catch (InstantiationException e) { 253 | e.printStackTrace(); 254 | } catch (IllegalAccessException e) { 255 | e.printStackTrace(); 256 | } catch (ClassNotFoundException e) { 257 | e.printStackTrace(); 258 | } 259 | break; 260 | } 261 | } 262 | return fragment; 263 | } 264 | 265 | private void hideAllFragment() { 266 | if (mViewHolderList == null || mViewHolderList.size() == 0) { 267 | return; 268 | } 269 | FragmentTransaction transaction = mFragmentActivity.getFragmentManager().beginTransaction(); 270 | 271 | for (ViewHolder holder : mViewHolderList) { 272 | Fragment fragment = mFragmentActivity.getFragmentManager().findFragmentByTag(holder.tag); 273 | if (fragment != null && !fragment.isHidden()) { 274 | transaction.hide(fragment); 275 | } 276 | } 277 | transaction.commit(); 278 | } 279 | 280 | public void setSelectedTabTextColor(ColorStateList selectedTextColor) { 281 | mSelectedTextColor = selectedTextColor; 282 | } 283 | 284 | public void setSelectedTabTextColor(int color) { 285 | mSelectedTextColor = ColorStateList.valueOf(color); 286 | } 287 | 288 | public void setTabTextColor(ColorStateList color) { 289 | mNormalTextColor = color; 290 | } 291 | 292 | public void setTabTextColor(int color) { 293 | mNormalTextColor = ColorStateList.valueOf(color); 294 | } 295 | 296 | public void setFrameLayoutId(int frameLayoutId) { 297 | mMainContentLayoutId = frameLayoutId; 298 | } 299 | 300 | public void onRestoreInstanceState(Bundle savedInstanceState) { 301 | if (savedInstanceState != null) { 302 | mRestoreTag = savedInstanceState.getString(KEY_CURRENT_TAG); 303 | } 304 | } 305 | 306 | public void onSaveInstanceState(Bundle outState) { 307 | outState.putString(KEY_CURRENT_TAG, mCurrentTag); 308 | } 309 | 310 | private static class ViewHolder { 311 | public String tag; 312 | public TabParam pageParam; 313 | public ImageView tabIcon; 314 | public TextView tabTitle; 315 | public Class fragmentClass; 316 | public int tabIndex; 317 | } 318 | 319 | 320 | public static class TabParam { 321 | public int backgroundColor = android.R.color.white; 322 | public int iconResId; 323 | public int iconSelectedResId; 324 | public int titleStringRes; 325 | // public int tabViewResId; 326 | public String title; 327 | 328 | public TabParam(int iconResId, int iconSelectedResId, String title) { 329 | this.iconResId = iconResId; 330 | this.iconSelectedResId = iconSelectedResId; 331 | this.title = title; 332 | } 333 | 334 | public TabParam(int iconResId, int iconSelectedResId, int titleStringRes) { 335 | this.iconResId = iconResId; 336 | this.iconSelectedResId = iconSelectedResId; 337 | this.titleStringRes = titleStringRes; 338 | } 339 | 340 | public TabParam(int backgroundColor, int iconResId, int iconSelectedResId, int titleStringRes) { 341 | this.backgroundColor = backgroundColor; 342 | this.iconResId = iconResId; 343 | this.iconSelectedResId = iconSelectedResId; 344 | this.titleStringRes = titleStringRes; 345 | } 346 | 347 | public TabParam(int backgroundColor, int iconResId, int iconSelectedResId, String title) { 348 | this.backgroundColor = backgroundColor; 349 | this.iconResId = iconResId; 350 | this.iconSelectedResId = iconSelectedResId; 351 | this.title = title; 352 | } 353 | } 354 | 355 | 356 | public interface OnTabSelectedListener { 357 | void onTabSelected(ViewHolder holder); 358 | } 359 | 360 | public void setTabSelectListener(OnTabSelectedListener tabSelectListener) { 361 | mTabSelectListener = tabSelectListener; 362 | } 363 | 364 | public void setDefaultSelectedTab(int index) { 365 | if (index >= 0 && index < mViewHolderList.size()) { 366 | mDefaultSelectedTab = index; 367 | } 368 | } 369 | 370 | public void setCurrentSelectedTab(int index) { 371 | if (index >= 0 && index < mViewHolderList.size()) { 372 | ViewHolder holder = mViewHolderList.get(index); 373 | showFragment(holder); 374 | } 375 | } 376 | 377 | public int getCurrentSelectedTab(){ 378 | return mCurrentSelectedTab; 379 | } 380 | 381 | 382 | 383 | } 384 | -------------------------------------------------------------------------------- /app/src/main/java/com/handmall/tabbar/widget/PullToRefresh/PullToRefreshView.java: -------------------------------------------------------------------------------- 1 | package com.handmall.tabbar.widget.PullToRefresh; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.support.annotation.NonNull; 6 | import android.support.v4.view.MotionEventCompat; 7 | import android.support.v4.view.ViewCompat; 8 | import android.util.AttributeSet; 9 | import android.view.MotionEvent; 10 | import android.view.View; 11 | import android.view.ViewConfiguration; 12 | import android.view.ViewGroup; 13 | import android.view.animation.Animation; 14 | import android.view.animation.DecelerateInterpolator; 15 | import android.view.animation.Interpolator; 16 | import android.view.animation.Transformation; 17 | import android.widget.AbsListView; 18 | import android.widget.ImageView; 19 | 20 | import com.handmall.tabbar.R; 21 | import com.handmall.tabbar.widget.PullToRefresh.refresh_view.BaseRefreshView; 22 | import com.handmall.tabbar.widget.PullToRefresh.refresh_view.SunRefreshView; 23 | import com.handmall.tabbar.widget.PullToRefresh.util.Logger; 24 | import com.handmall.tabbar.widget.PullToRefresh.util.Utils; 25 | 26 | import java.security.InvalidParameterException; 27 | 28 | public class PullToRefreshView extends ViewGroup { 29 | 30 | private static final int DRAG_MAX_DISTANCE = 120; 31 | /** 32 | * 下拉拖拽阻尼 33 | */ 34 | private static final float DRAG_RATE = .5f; 35 | private static final float DECELERATE_INTERPOLATION_FACTOR = 2f; 36 | 37 | public static final int STYLE_SUN = 0; 38 | public static final int MAX_OFFSET_ANIMATION_DURATION = 700; 39 | 40 | private static final int INVALID_POINTER = -1; 41 | 42 | private View mTarget; 43 | private ImageView mRefreshView; 44 | private Interpolator mDecelerateInterpolator; 45 | private int mTouchSlop; 46 | /** 47 | * 触发刷新的下拉距离,也是mRefreshView刷新时停留的高度 48 | */ 49 | private int mTotalDragDistance; 50 | private BaseRefreshView mBaseRefreshView; 51 | /** 52 | * 下拉进度 53 | */ 54 | private float mCurrentDragPercent; 55 | /** 56 | * mCurrentOffsetTop=mTarget.getTop();内容view的top值 57 | */ 58 | private int mCurrentOffsetTop; 59 | 60 | public boolean isRefreshing() { 61 | return mRefreshing; 62 | } 63 | 64 | private boolean mRefreshing; 65 | private int mActivePointerId; 66 | private boolean mIsBeingDragged; 67 | private float mInitialMotionY; 68 | private int mFrom; 69 | private float mFromDragPercent; 70 | private boolean mNotify; 71 | private OnRefreshListener mListener; 72 | 73 | private int mTargetPaddingTop; 74 | private int mTargetPaddingBottom; 75 | private int mTargetPaddingRight; 76 | private int mTargetPaddingLeft; 77 | 78 | private int finishRefreshToPauseDuration = 0; 79 | 80 | public PullToRefreshView(Context context) { 81 | this(context, null); 82 | } 83 | 84 | public PullToRefreshView(Context context, AttributeSet attrs) { 85 | super(context, attrs); 86 | TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RefreshView); 87 | final int type = a.getInteger(R.styleable.RefreshView_type, STYLE_SUN); 88 | a.recycle(); 89 | 90 | mDecelerateInterpolator = new DecelerateInterpolator(DECELERATE_INTERPOLATION_FACTOR); 91 | mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); 92 | mTotalDragDistance = Utils.convertDpToPixel(context, DRAG_MAX_DISTANCE); 93 | 94 | mRefreshView = new ImageView(context); 95 | 96 | setRefreshStyle(type); 97 | 98 | addView(mRefreshView); 99 | 100 | //在构造函数上加上这句,防止自定义View的onDraw方法不执行的问题 101 | setWillNotDraw(false); 102 | ViewCompat.setChildrenDrawingOrderEnabled(this, true); 103 | } 104 | 105 | public void setRefreshStyle(int type) { 106 | setRefreshing(false); 107 | switch (type) { 108 | case STYLE_SUN: 109 | mBaseRefreshView = new SunRefreshView(getContext(), this); 110 | break; 111 | default: 112 | throw new InvalidParameterException("Type does not exist"); 113 | } 114 | mRefreshView.setImageDrawable(mBaseRefreshView); 115 | } 116 | 117 | /** 118 | * This method sets padding for the refresh (progress) view. 119 | */ 120 | public void setRefreshViewPadding(int left, int top, int right, int bottom) { 121 | mRefreshView.setPadding(left, top, right, bottom); 122 | } 123 | 124 | public int getTotalDragDistance() { 125 | return mTotalDragDistance; 126 | } 127 | 128 | @Override 129 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 130 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 131 | 132 | ensureTarget(); 133 | 134 | if (mTarget == null) 135 | return; 136 | 137 | widthMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth() - getPaddingRight() - getPaddingLeft(), MeasureSpec.EXACTLY); 138 | heightMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(), MeasureSpec.EXACTLY); 139 | mTarget.measure(widthMeasureSpec, heightMeasureSpec); 140 | mRefreshView.measure(widthMeasureSpec, heightMeasureSpec); 141 | } 142 | 143 | private void ensureTarget() { 144 | if (mTarget != null) 145 | return; 146 | if (getChildCount() > 0) { 147 | for (int i = 0; i < getChildCount(); i++) { 148 | View child = getChildAt(i); 149 | if (child != mRefreshView) { 150 | mTarget = child; 151 | mTargetPaddingBottom = mTarget.getPaddingBottom(); 152 | mTargetPaddingLeft = mTarget.getPaddingLeft(); 153 | mTargetPaddingRight = mTarget.getPaddingRight(); 154 | mTargetPaddingTop = mTarget.getPaddingTop(); 155 | } 156 | } 157 | } 158 | } 159 | 160 | /** 161 | * 该函数只干两件事 162 | * 1.记录手指按下的坐标 163 | * 2.判断是否拦截处理事件 164 | */ 165 | @Override 166 | public boolean onInterceptTouchEvent(MotionEvent ev) { 167 | //如果被禁用、到达顶部、正在刷新时,不拦截点击事件,不做任何处理 168 | if (!isEnabled() || canChildScrollUp() || mRefreshing) { 169 | return false; 170 | } 171 | 172 | final int action = MotionEventCompat.getActionMasked(ev); 173 | 174 | switch (action) { 175 | //手指按下,记录点击坐标 176 | case MotionEvent.ACTION_DOWN: 177 | // setTargetOffsetTop(0, true); 178 | mActivePointerId = MotionEventCompat.getPointerId(ev, 0); 179 | mIsBeingDragged = false; 180 | final float initialMotionY = getMotionEventY(ev, mActivePointerId); 181 | if (initialMotionY == -1) { 182 | return false; 183 | } 184 | mInitialMotionY = initialMotionY; 185 | break; 186 | case MotionEvent.ACTION_MOVE: 187 | if (mActivePointerId == INVALID_POINTER) { 188 | return false; 189 | } 190 | final float y = getMotionEventY(ev, mActivePointerId); 191 | if (y == -1) { 192 | return false; 193 | } 194 | final float yDiff = y - mInitialMotionY; 195 | //如果是滑动动作,将标志mIsBeingDragged置为true 196 | if (yDiff > mTouchSlop && !mIsBeingDragged) { 197 | mIsBeingDragged = true; 198 | } 199 | break; 200 | //手指松开,标志复位 201 | case MotionEvent.ACTION_UP: 202 | case MotionEvent.ACTION_CANCEL: 203 | mIsBeingDragged = false; 204 | mActivePointerId = INVALID_POINTER; 205 | break; 206 | case MotionEventCompat.ACTION_POINTER_UP: 207 | onSecondaryPointerUp(ev); 208 | break; 209 | } 210 | 211 | //如果是正在被下拉拖动,拦截,不往下传递;反之,你懂的 212 | return mIsBeingDragged; 213 | } 214 | 215 | @Override 216 | public boolean onTouchEvent(@NonNull MotionEvent ev) { 217 | 218 | //如果不是在被下拉拖动,不处理,直接返回 219 | if (!mIsBeingDragged) { 220 | return super.onTouchEvent(ev); 221 | } 222 | 223 | final int action = MotionEventCompat.getActionMasked(ev); 224 | 225 | switch (action) { 226 | case MotionEvent.ACTION_MOVE: { 227 | final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId); 228 | if (pointerIndex < 0) { 229 | return false; 230 | } 231 | 232 | final float y = MotionEventCompat.getY(ev, pointerIndex); 233 | final float yDiff = y - mInitialMotionY; 234 | //未松手前,总共下拉的距离 float 235 | final float scrollTop = yDiff * DRAG_RATE; 236 | mCurrentDragPercent = scrollTop / mTotalDragDistance; 237 | if (mCurrentDragPercent < 0) { 238 | return false; 239 | } 240 | float boundedDragPercent = Math.min(1f, Math.abs(mCurrentDragPercent)); 241 | float extraOS = Math.abs(scrollTop) - mTotalDragDistance; 242 | float slingshotDist = mTotalDragDistance; 243 | float tensionSlingshotPercent = Math.max(0, 244 | Math.min(extraOS, slingshotDist * 2) / slingshotDist); 245 | float tensionPercent = (float) ((tensionSlingshotPercent / 4) - Math.pow( 246 | tensionSlingshotPercent / 4, 2)) * 2f; 247 | float extraMove = (slingshotDist) * tensionPercent / 2; 248 | //未松手前,target下拉的总高度 int 249 | // int targetY = (int) scrollTop; //可以替代下面这句代码,效果一样 250 | int targetY = (int) ((slingshotDist * boundedDragPercent) + extraMove); 251 | Logger.d("targetY:" + targetY); 252 | Logger.d("scrollTop:" + scrollTop); 253 | mBaseRefreshView.setPercent(mCurrentDragPercent, true); 254 | if (mListener != null) { 255 | mListener.ondragDistanceChange(scrollTop, mCurrentDragPercent, (scrollTop - mCurrentOffsetTop) / mTotalDragDistance); 256 | } 257 | //调整更新位置,传过去的值是每次的偏移量 258 | setTargetOffsetTop(targetY - mCurrentOffsetTop, true); 259 | break; 260 | } 261 | //做多指触控处理 262 | case MotionEventCompat.ACTION_POINTER_DOWN: 263 | //将最后一只按下的手指作为ActivePointer 264 | final int index = MotionEventCompat.getActionIndex(ev); 265 | mActivePointerId = MotionEventCompat.getPointerId(ev, index); 266 | break; 267 | case MotionEventCompat.ACTION_POINTER_UP: 268 | onSecondaryPointerUp(ev); 269 | break; 270 | //手指松开! 271 | case MotionEvent.ACTION_UP: 272 | case MotionEvent.ACTION_CANCEL: { 273 | //排除是无关手指 274 | if (mActivePointerId == INVALID_POINTER) { 275 | return false; 276 | } 277 | final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId); 278 | final float y = MotionEventCompat.getY(ev, pointerIndex); 279 | //计算松开瞬间下拉的距离 280 | final float overScrollTop = (y - mInitialMotionY) * DRAG_RATE; 281 | //标志复位 282 | mIsBeingDragged = false; 283 | mActivePointerId = INVALID_POINTER; 284 | 285 | if (overScrollTop > mTotalDragDistance) {//触发刷新 286 | setRefreshing(true, true); 287 | } else {//回滚 288 | mRefreshing = false; 289 | animateOffsetToStartPosition(); 290 | } 291 | return false;//系列点击事件已经处理完,将处理权交还mTarget 292 | } 293 | } 294 | 295 | return true;//该系列点击事件未处理完,消耗此系列事件 296 | } 297 | 298 | /** 299 | * 回滚动画 300 | */ 301 | private void animateOffsetToStartPosition() { 302 | mFrom = mCurrentOffsetTop; 303 | mFromDragPercent = mCurrentDragPercent; 304 | long animationDuration = Math.abs((long) (MAX_OFFSET_ANIMATION_DURATION * mFromDragPercent)); 305 | 306 | //当动画开始,通过applyTransformation(float interpolatedTime, Transformation t)方法的interpolatedTime参数(0.0~1.0) 307 | //内部调用setTargetOffsetTop(offset, false);两者更新位置和RefreshView的加载动画 308 | //当动画结束时,更新mCurrentOffsetTop=mTarget.getTop(); 309 | mAnimateToStartPosition.reset(); 310 | mAnimateToStartPosition.setDuration(animationDuration); 311 | mAnimateToStartPosition.setInterpolator(mDecelerateInterpolator); 312 | mAnimateToStartPosition.setAnimationListener(mToStartListener); 313 | mRefreshView.clearAnimation(); 314 | mRefreshView.startAnimation(mAnimateToStartPosition); 315 | } 316 | 317 | private void animateOffsetToCorrectPosition() { 318 | mFrom = mCurrentOffsetTop; 319 | mFromDragPercent = mCurrentDragPercent; 320 | 321 | //动画上调至mTotalDragDistance这个高度 322 | mAnimateToCorrectPosition.reset(); 323 | mAnimateToCorrectPosition.setDuration(MAX_OFFSET_ANIMATION_DURATION); 324 | mAnimateToCorrectPosition.setInterpolator(mDecelerateInterpolator); 325 | mRefreshView.clearAnimation(); 326 | mRefreshView.startAnimation(mAnimateToCorrectPosition); 327 | 328 | if (mRefreshing) {//开始刷新 329 | mBaseRefreshView.start(); 330 | if (mNotify) { 331 | if (mListener != null) { 332 | mListener.onRefresh(); 333 | } 334 | } 335 | } else {//停止刷新 336 | mBaseRefreshView.stop(); 337 | animateOffsetToStartPosition(); 338 | } 339 | //更新mCurrentOffsetTop 340 | mCurrentOffsetTop = mTarget.getTop(); 341 | // mTarget.setPadding(mTargetPaddingLeft, mTargetPaddingTop, mTargetPaddingRight, mTotalDragDistance); 342 | } 343 | 344 | private final Animation mAnimateToStartPosition = new Animation() { 345 | @Override 346 | public void applyTransformation(float interpolatedTime, Transformation t) { 347 | moveToStart(interpolatedTime); 348 | } 349 | }; 350 | 351 | private final Animation mAnimateToCorrectPosition = new Animation() { 352 | @Override 353 | public void applyTransformation(float interpolatedTime, Transformation t) { 354 | int targetTop; 355 | int endTarget = mTotalDragDistance; 356 | targetTop = mFrom + (int) ((endTarget - mFrom) * interpolatedTime); 357 | int offset = targetTop - mTarget.getTop(); 358 | 359 | mCurrentDragPercent = mFromDragPercent - (mFromDragPercent - 1.0f) * interpolatedTime; 360 | mBaseRefreshView.setPercent(mCurrentDragPercent, false); 361 | if (mListener != null) { 362 | float pos = mFrom + (endTarget - mFrom) * interpolatedTime; 363 | mListener.ondragDistanceChange(pos, 364 | mCurrentDragPercent, (pos - mTarget.getTop()) / mTotalDragDistance); 365 | } 366 | setTargetOffsetTop(offset, false /* requires update */); 367 | } 368 | }; 369 | 370 | private void moveToStart(float interpolatedTime) { 371 | int targetTop = mFrom - (int) (mFrom * interpolatedTime); 372 | float targetPercent = mFromDragPercent * (1.0f - interpolatedTime); 373 | //计算偏移量 374 | int offset = targetTop - mTarget.getTop(); 375 | 376 | //更新RefreshView加载动画 377 | mCurrentDragPercent = targetPercent; 378 | mBaseRefreshView.setPercent(mCurrentDragPercent, true); 379 | if (mListener != null) { 380 | float pos = mFrom - mFrom * interpolatedTime; 381 | mListener.ondragDistanceChange(pos, 382 | mCurrentDragPercent, (pos - mTarget.getTop()) / mTotalDragDistance); 383 | } 384 | 385 | //更新mTarget和mRefreshView的位置 386 | // mTarget.setPadding(mTargetPaddingLeft, mTargetPaddingTop, mTargetPaddingRight, mTargetPaddingBottom + targetTop); 387 | setTargetOffsetTop(offset, false); 388 | 389 | } 390 | 391 | public void setRefreshing(boolean refreshing) { 392 | if (mRefreshing != refreshing) { 393 | setRefreshing(refreshing, false /* notify */); 394 | } 395 | 396 | } 397 | 398 | private void setRefreshing(boolean refreshing, final boolean notify) { 399 | if (mRefreshing != refreshing) { 400 | mNotify = notify; 401 | ensureTarget(); 402 | mRefreshing = refreshing; 403 | if (mRefreshing) { 404 | //开始刷新 405 | mBaseRefreshView.setPercent(1f, true); 406 | animateOffsetToCorrectPosition();//位置上调到合适的位置 407 | } else { 408 | //刷新完成 409 | if (mListener!=null) 410 | mListener.onFinish(); 411 | try { 412 | //停顿半秒 413 | Thread.sleep(finishRefreshToPauseDuration); 414 | } catch (InterruptedException e) { 415 | e.printStackTrace(); 416 | } 417 | animateOffsetToStartPosition(); 418 | } 419 | } 420 | } 421 | 422 | private final Animation.AnimationListener mToStartListener = new Animation.AnimationListener() { 423 | @Override 424 | public void onAnimationStart(Animation animation) { 425 | } 426 | 427 | @Override 428 | public void onAnimationRepeat(Animation animation) { 429 | } 430 | 431 | @Override 432 | public void onAnimationEnd(Animation animation) { 433 | mBaseRefreshView.stop();//停止RefreshView的加载动画 434 | mCurrentOffsetTop = mTarget.getTop();//更新mCurrentOffsetTop 435 | } 436 | }; 437 | 438 | /** 439 | * 处理多指触控的点击事件 440 | */ 441 | private void onSecondaryPointerUp(MotionEvent ev) { 442 | final int pointerIndex = MotionEventCompat.getActionIndex(ev); 443 | final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex); 444 | if (pointerId == mActivePointerId) { 445 | final int newPointerIndex = pointerIndex == 0 ? 1 : 0; 446 | mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex); 447 | } 448 | } 449 | 450 | private float getMotionEventY(MotionEvent ev, int activePointerId) { 451 | final int index = MotionEventCompat.findPointerIndex(ev, activePointerId); 452 | if (index < 0) { 453 | return -1; 454 | } 455 | return MotionEventCompat.getY(ev, index); 456 | } 457 | 458 | /** 459 | * 通过调用offsetTopAndBottom方法 460 | * 更新mTarget和mBaseRefreshView的位置 461 | * 更新target下拉高度--mCurrentOffsetTop 462 | * 463 | * @param offset 偏移位移 464 | * @param requiresUpdate 时候invalidate() 465 | */ 466 | private void setTargetOffsetTop(int offset, boolean requiresUpdate) { 467 | mTarget.offsetTopAndBottom(offset); 468 | mBaseRefreshView.offsetTopAndBottom(offset); 469 | mCurrentOffsetTop = mTarget.getTop(); 470 | if (requiresUpdate && android.os.Build.VERSION.SDK_INT < 11) { 471 | invalidate(); 472 | } 473 | } 474 | 475 | private boolean canChildScrollUp() { 476 | if (android.os.Build.VERSION.SDK_INT < 14) { 477 | if (mTarget instanceof AbsListView) { 478 | final AbsListView absListView = (AbsListView) mTarget; 479 | return absListView.getChildCount() > 0 480 | && (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0) 481 | .getTop() < absListView.getPaddingTop()); 482 | } else { 483 | return mTarget.getScrollY() > 0; 484 | } 485 | } else { 486 | return ViewCompat.canScrollVertically(mTarget, -1); 487 | } 488 | } 489 | 490 | @Override 491 | protected void onLayout(boolean changed, int l, int t, int r, int b) { 492 | 493 | ensureTarget(); 494 | if (mTarget == null) 495 | return; 496 | 497 | int height = getMeasuredHeight(); 498 | int width = getMeasuredWidth(); 499 | int left = getPaddingLeft(); 500 | int top = getPaddingTop(); 501 | int right = getPaddingRight(); 502 | int bottom = getPaddingBottom(); 503 | 504 | //mTarget MATCH_PARENT 505 | mTarget.layout(left, top + mCurrentOffsetTop, left + width - right, top + height - bottom + mCurrentOffsetTop); 506 | //mRefreshView隐藏在mTarget的下面 507 | mRefreshView.layout(left, top, left + width - right, top + height - bottom); 508 | } 509 | 510 | public void setOnRefreshListener(OnRefreshListener listener) { 511 | mListener = listener; 512 | } 513 | 514 | public void setFinishRefreshToPauseDuration(int finishRefreshToPauseDuration) { 515 | this.finishRefreshToPauseDuration = finishRefreshToPauseDuration; 516 | } 517 | 518 | public interface OnRefreshListener { 519 | void onRefresh(); 520 | /** 521 | * 不要在此进行耗时的操作 522 | */ 523 | void onFinish(); 524 | void ondragDistanceChange(float distance, float percent, float offset); 525 | } 526 | 527 | public static class OnRefreshListenerAdapter implements OnRefreshListener{ 528 | @Override 529 | public void onRefresh() {} 530 | 531 | @Override 532 | public void onFinish() { 533 | 534 | } 535 | 536 | @Override 537 | public void ondragDistanceChange(float distance, float percent, float offset) {} 538 | } 539 | } -------------------------------------------------------------------------------- /app/src/main/java/com/handmall/tabbar/widget/PullToRefresh/refresh_view/BaseRefreshView.java: -------------------------------------------------------------------------------- 1 | package com.handmall.tabbar.widget.PullToRefresh.refresh_view; 2 | 3 | import android.content.Context; 4 | import android.graphics.ColorFilter; 5 | import android.graphics.PixelFormat; 6 | import android.graphics.drawable.Animatable; 7 | import android.graphics.drawable.Drawable; 8 | import android.support.annotation.NonNull; 9 | 10 | import com.handmall.tabbar.widget.PullToRefresh.PullToRefreshView; 11 | 12 | public abstract class BaseRefreshView extends Drawable implements Drawable.Callback, Animatable { 13 | 14 | private PullToRefreshView mRefreshLayout; 15 | private boolean mEndOfRefreshing; 16 | 17 | public BaseRefreshView(Context context, PullToRefreshView layout) { 18 | mRefreshLayout = layout; 19 | } 20 | 21 | public Context getContext() { 22 | return mRefreshLayout != null ? mRefreshLayout.getContext() : null; 23 | } 24 | 25 | public PullToRefreshView getRefreshLayout() { 26 | return mRefreshLayout; 27 | } 28 | 29 | public abstract void setPercent(float percent, boolean invalidate); 30 | 31 | public abstract void offsetTopAndBottom(int offset); 32 | 33 | @Override 34 | public void invalidateDrawable(@NonNull Drawable who) { 35 | final Callback callback = getCallback(); 36 | if (callback != null) { 37 | callback.invalidateDrawable(this); 38 | } 39 | } 40 | 41 | @Override 42 | public void scheduleDrawable(Drawable who, Runnable what, long when) { 43 | final Callback callback = getCallback(); 44 | if (callback != null) { 45 | callback.scheduleDrawable(this, what, when); 46 | } 47 | } 48 | 49 | @Override 50 | public void unscheduleDrawable(Drawable who, Runnable what) { 51 | final Callback callback = getCallback(); 52 | if (callback != null) { 53 | callback.unscheduleDrawable(this, what); 54 | } 55 | } 56 | 57 | @Override 58 | public int getOpacity() { 59 | return PixelFormat.TRANSLUCENT; 60 | } 61 | 62 | @Override 63 | public void setAlpha(int alpha) { 64 | 65 | } 66 | 67 | @Override 68 | public void setColorFilter(ColorFilter cf) { 69 | 70 | } 71 | 72 | /** 73 | * Our animation depend on type of current work of refreshing. 74 | * We should to do different things when it's end of refreshing 75 | * 76 | * @param endOfRefreshing - we will check current state of refresh with this 77 | */ 78 | public void setEndOfRefreshing(boolean endOfRefreshing) { 79 | mEndOfRefreshing = endOfRefreshing; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /app/src/main/java/com/handmall/tabbar/widget/PullToRefresh/refresh_view/SunRefreshView.java: -------------------------------------------------------------------------------- 1 | package com.handmall.tabbar.widget.PullToRefresh.refresh_view; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.graphics.BitmapFactory; 6 | import android.graphics.Canvas; 7 | import android.graphics.Matrix; 8 | import android.graphics.Rect; 9 | import android.graphics.drawable.Animatable; 10 | import android.view.animation.Animation; 11 | import android.view.animation.Interpolator; 12 | import android.view.animation.LinearInterpolator; 13 | import android.view.animation.Transformation; 14 | 15 | import com.handmall.tabbar.R; 16 | import com.handmall.tabbar.widget.PullToRefresh.PullToRefreshView; 17 | import com.handmall.tabbar.widget.PullToRefresh.util.Utils; 18 | 19 | /** 20 | * Created by Oleksii Shliama on 22/12/2014. 21 | * https://dribbble.com/shots/1650317-Pull-to-Refresh-Rentals 22 | */ 23 | public class SunRefreshView extends BaseRefreshView implements Animatable { 24 | 25 | private static final float SCALE_START_PERCENT = 0.5f; 26 | private static final int ANIMATION_DURATION = 1000; 27 | 28 | private final static float SKY_RATIO = 0.65f; 29 | private static final float SKY_INITIAL_SCALE = 1.05f; 30 | 31 | private final static float TOWN_RATIO = 0.22f; 32 | private static final float TOWN_INITIAL_SCALE = 1.20f; 33 | private static final float TOWN_FINAL_SCALE = 1.30f; 34 | 35 | private static final float SUN_FINAL_SCALE = 0.75f; 36 | private static final float SUN_INITIAL_ROTATE_GROWTH = 1.2f; 37 | private static final float SUN_FINAL_ROTATE_GROWTH = 1.5f; 38 | 39 | private static final Interpolator LINEAR_INTERPOLATOR = new LinearInterpolator(); 40 | 41 | private PullToRefreshView mParent; 42 | private Matrix mMatrix; 43 | private Animation mAnimation; 44 | 45 | private int mTop; 46 | private int mScreenWidth; 47 | 48 | private int mSkyHeight; 49 | private float mSkyTopOffset; 50 | private float mSkyMoveOffset; 51 | 52 | private int mTownHeight; 53 | private float mTownInitialTopOffset; 54 | private float mTownFinalTopOffset; 55 | private float mTownMoveOffset; 56 | 57 | private int mSunSize = 100; 58 | private float mSunLeftOffset; 59 | private float mSunTopOffset; 60 | 61 | private float mPercent = 0.0f; 62 | private float mRotate = 0.0f; 63 | 64 | private Bitmap mSky; 65 | private Bitmap mSun; 66 | private Bitmap mTown; 67 | 68 | private boolean isRefreshing = false; 69 | 70 | public SunRefreshView(Context context, final PullToRefreshView parent) { 71 | super(context, parent); 72 | mParent = parent; 73 | mMatrix = new Matrix(); 74 | 75 | setupAnimations(); 76 | parent.post(new Runnable() { 77 | @Override 78 | public void run() { 79 | initiateDimens(parent.getWidth()); 80 | } 81 | }); 82 | } 83 | 84 | public void initiateDimens(int viewWidth) { 85 | if (viewWidth <= 0 || viewWidth == mScreenWidth) return; 86 | 87 | mScreenWidth = viewWidth; 88 | mSkyHeight = (int) (SKY_RATIO * mScreenWidth); 89 | mSkyTopOffset = (mSkyHeight * 0.38f); 90 | mSkyMoveOffset = Utils.convertDpToPixel(getContext(), 15); 91 | 92 | mTownHeight = (int) (TOWN_RATIO * mScreenWidth); 93 | mTownInitialTopOffset = (mParent.getTotalDragDistance() - mTownHeight * TOWN_INITIAL_SCALE); 94 | mTownFinalTopOffset = (mParent.getTotalDragDistance() - mTownHeight * TOWN_FINAL_SCALE); 95 | mTownMoveOffset = Utils.convertDpToPixel(getContext(), 10); 96 | 97 | mSunLeftOffset = 0.3f * (float) mScreenWidth; 98 | mSunTopOffset = (mParent.getTotalDragDistance() * 0.1f); 99 | 100 | mTop = -mParent.getTotalDragDistance(); 101 | 102 | createBitmaps(); 103 | } 104 | 105 | private void createBitmaps() { 106 | final BitmapFactory.Options options = new BitmapFactory.Options(); 107 | options.inPreferredConfig = Bitmap.Config.RGB_565; 108 | 109 | mSky = BitmapFactory.decodeResource(getContext().getResources(), R.drawable.sky, options); 110 | mSky = Bitmap.createScaledBitmap(mSky, mScreenWidth, mSkyHeight, true); 111 | mTown = BitmapFactory.decodeResource(getContext().getResources(), R.drawable.buildings, options); 112 | mTown = Bitmap.createScaledBitmap(mTown, mScreenWidth, (int) (mScreenWidth * TOWN_RATIO), true); 113 | mSun = BitmapFactory.decodeResource(getContext().getResources(), R.drawable.sun, options); 114 | mSun = Bitmap.createScaledBitmap(mSun, mSunSize, mSunSize, true); 115 | } 116 | 117 | @Override 118 | public void setPercent(float percent, boolean invalidate) { 119 | setPercent(percent); 120 | if (invalidate) setRotate(percent); 121 | } 122 | 123 | @Override 124 | public void offsetTopAndBottom(int offset) { 125 | mTop += offset; 126 | invalidateSelf(); 127 | } 128 | 129 | @Override 130 | public void draw(Canvas canvas) { 131 | if (mScreenWidth <= 0) return; 132 | 133 | final int saveCount = canvas.save(); 134 | 135 | canvas.translate(0, mTop); 136 | canvas.clipRect(0, -mTop, mScreenWidth, mParent.getTotalDragDistance()); 137 | 138 | drawSky(canvas); 139 | drawSun(canvas); 140 | drawTown(canvas); 141 | 142 | canvas.restoreToCount(saveCount); 143 | } 144 | 145 | private void drawSky(Canvas canvas) { 146 | Matrix matrix = mMatrix; 147 | matrix.reset(); 148 | 149 | float dragPercent = Math.min(1f, Math.abs(mPercent)); 150 | 151 | float skyScale; 152 | float scalePercentDelta = dragPercent - SCALE_START_PERCENT; 153 | if (scalePercentDelta > 0) { 154 | /** Change skyScale between {@link #SKY_INITIAL_SCALE} and 1.0f depending on {@link #mPercent} */ 155 | float scalePercent = scalePercentDelta / (1.0f - SCALE_START_PERCENT); 156 | skyScale = SKY_INITIAL_SCALE - (SKY_INITIAL_SCALE - 1.0f) * scalePercent; 157 | } else { 158 | skyScale = SKY_INITIAL_SCALE; 159 | } 160 | 161 | float offsetX = -(mScreenWidth * skyScale - mScreenWidth) / 2.0f; 162 | float offsetY = (1.0f - dragPercent) * mParent.getTotalDragDistance() - mSkyTopOffset // Offset canvas moving 163 | - mSkyHeight * (skyScale - 1.0f) / 2 // Offset sky scaling 164 | + mSkyMoveOffset * dragPercent; // Give it a little move top -> bottom 165 | 166 | matrix.postScale(skyScale, skyScale); 167 | matrix.postTranslate(offsetX, offsetY); 168 | canvas.drawBitmap(mSky, matrix, null); 169 | } 170 | 171 | private void drawTown(Canvas canvas) { 172 | Matrix matrix = mMatrix; 173 | matrix.reset(); 174 | 175 | float dragPercent = Math.min(1f, Math.abs(mPercent)); 176 | 177 | float townScale; 178 | float townTopOffset; 179 | float townMoveOffset; 180 | float scalePercentDelta = dragPercent - SCALE_START_PERCENT; 181 | if (scalePercentDelta > 0) { 182 | /** 183 | * Change townScale between {@link #TOWN_INITIAL_SCALE} and {@link #TOWN_FINAL_SCALE} depending on {@link #mPercent} 184 | * Change townTopOffset between {@link #mTownInitialTopOffset} and {@link #mTownFinalTopOffset} depending on {@link #mPercent} 185 | */ 186 | float scalePercent = scalePercentDelta / (1.0f - SCALE_START_PERCENT); 187 | townScale = TOWN_INITIAL_SCALE + (TOWN_FINAL_SCALE - TOWN_INITIAL_SCALE) * scalePercent; 188 | townTopOffset = mTownInitialTopOffset - (mTownFinalTopOffset - mTownInitialTopOffset) * scalePercent; 189 | townMoveOffset = mTownMoveOffset * (1.0f - scalePercent); 190 | } else { 191 | float scalePercent = dragPercent / SCALE_START_PERCENT; 192 | townScale = TOWN_INITIAL_SCALE; 193 | townTopOffset = mTownInitialTopOffset; 194 | townMoveOffset = mTownMoveOffset * scalePercent; 195 | } 196 | 197 | float offsetX = -(mScreenWidth * townScale - mScreenWidth) / 2.0f; 198 | float offsetY = (1.0f - dragPercent) * mParent.getTotalDragDistance() // Offset canvas moving 199 | + townTopOffset 200 | - mTownHeight * (townScale - 1.0f) / 2 // Offset town scaling 201 | + townMoveOffset; // Give it a little move 202 | 203 | matrix.postScale(townScale, townScale); 204 | matrix.postTranslate(offsetX, offsetY); 205 | 206 | canvas.drawBitmap(mTown, matrix, null); 207 | } 208 | 209 | private void drawSun(Canvas canvas) { 210 | Matrix matrix = mMatrix; 211 | matrix.reset(); 212 | 213 | float dragPercent = mPercent; 214 | if (dragPercent > 1.0f) { // Slow down if pulling over set height 215 | dragPercent = (dragPercent + 9.0f) / 10; 216 | } 217 | 218 | float sunRadius = (float) mSunSize / 2.0f; 219 | float sunRotateGrowth = SUN_INITIAL_ROTATE_GROWTH; 220 | 221 | float offsetX = mSunLeftOffset; 222 | float offsetY = mSunTopOffset 223 | + (mParent.getTotalDragDistance() / 2) * (1.0f - dragPercent) // Move the sun up 224 | - mTop; // Depending on Canvas position 225 | 226 | float scalePercentDelta = dragPercent - SCALE_START_PERCENT; 227 | if (scalePercentDelta > 0) { 228 | float scalePercent = scalePercentDelta / (1.0f - SCALE_START_PERCENT); 229 | float sunScale = 1.0f - (1.0f - SUN_FINAL_SCALE) * scalePercent; 230 | sunRotateGrowth += (SUN_FINAL_ROTATE_GROWTH - SUN_INITIAL_ROTATE_GROWTH) * scalePercent; 231 | 232 | matrix.preTranslate(offsetX + (sunRadius - sunRadius * sunScale), offsetY * (2.0f - sunScale)); 233 | matrix.preScale(sunScale, sunScale); 234 | 235 | offsetX += sunRadius; 236 | offsetY = offsetY * (2.0f - sunScale) + sunRadius * sunScale; 237 | } else { 238 | matrix.postTranslate(offsetX, offsetY); 239 | 240 | offsetX += sunRadius; 241 | offsetY += sunRadius; 242 | } 243 | 244 | matrix.postRotate( 245 | (isRefreshing ? -360 : 360) * mRotate * (isRefreshing ? 1 : sunRotateGrowth), 246 | offsetX, 247 | offsetY); 248 | 249 | canvas.drawBitmap(mSun, matrix, null); 250 | } 251 | 252 | public void setPercent(float percent) { 253 | mPercent = percent; 254 | } 255 | 256 | public void setRotate(float rotate) { 257 | mRotate = rotate; 258 | invalidateSelf(); 259 | } 260 | 261 | public void resetOriginals() { 262 | setPercent(0); 263 | setRotate(0); 264 | } 265 | 266 | @Override 267 | protected void onBoundsChange(Rect bounds) { 268 | super.onBoundsChange(bounds); 269 | } 270 | 271 | @Override 272 | public void setBounds(int left, int top, int right, int bottom) { 273 | super.setBounds(left, top, right, mSkyHeight + top); 274 | } 275 | 276 | @Override 277 | public boolean isRunning() { 278 | return false; 279 | } 280 | 281 | @Override 282 | public void start() { 283 | mAnimation.reset(); 284 | isRefreshing = true; 285 | mParent.startAnimation(mAnimation); 286 | } 287 | 288 | @Override 289 | public void stop() { 290 | mParent.clearAnimation(); 291 | isRefreshing = false; 292 | resetOriginals(); 293 | } 294 | 295 | private void setupAnimations() { 296 | mAnimation = new Animation() { 297 | @Override 298 | public void applyTransformation(float interpolatedTime, Transformation t) { 299 | setRotate(interpolatedTime); 300 | } 301 | }; 302 | mAnimation.setRepeatCount(Animation.INFINITE); 303 | mAnimation.setRepeatMode(Animation.RESTART); 304 | mAnimation.setInterpolator(LINEAR_INTERPOLATOR); 305 | mAnimation.setDuration(ANIMATION_DURATION); 306 | } 307 | 308 | } 309 | -------------------------------------------------------------------------------- /app/src/main/java/com/handmall/tabbar/widget/PullToRefresh/util/Logger.java: -------------------------------------------------------------------------------- 1 | package com.handmall.tabbar.widget.PullToRefresh.util; 2 | 3 | import android.text.TextUtils; 4 | 5 | public final class Logger { 6 | 7 | private static final String TAG = "Phoenix"; 8 | 9 | /** 10 | * Set true or false if you want read logs or not 11 | */ 12 | private static boolean logEnabled_d = false; 13 | private static boolean logEnabled_i = false; 14 | private static boolean logEnabled_e = false; 15 | 16 | public static void d() { 17 | if (logEnabled_d) { 18 | android.util.Log.v(TAG, getLocation()); 19 | } 20 | } 21 | 22 | public static void d(String msg) { 23 | if (logEnabled_d) { 24 | android.util.Log.v(TAG, getLocation() + msg); 25 | } 26 | } 27 | 28 | public static void i(String msg) { 29 | if (logEnabled_i) { 30 | android.util.Log.i(TAG, getLocation() + msg); 31 | } 32 | } 33 | 34 | public static void i() { 35 | if (logEnabled_i) { 36 | android.util.Log.i(TAG, getLocation()); 37 | } 38 | } 39 | 40 | public static void e(String msg) { 41 | if (logEnabled_e) { 42 | android.util.Log.e(TAG, getLocation() + msg); 43 | } 44 | } 45 | 46 | public static void e(String msg, Throwable e) { 47 | if (logEnabled_e) { 48 | android.util.Log.e(TAG, getLocation() + msg, e); 49 | } 50 | } 51 | 52 | public static void e(Throwable e) { 53 | if (logEnabled_e) { 54 | android.util.Log.e(TAG, getLocation(), e); 55 | } 56 | } 57 | 58 | public static void e() { 59 | if (logEnabled_e) { 60 | android.util.Log.e(TAG, getLocation()); 61 | } 62 | } 63 | 64 | private static String getLocation() { 65 | final String className = Logger.class.getName(); 66 | final StackTraceElement[] traces = Thread.currentThread() 67 | .getStackTrace(); 68 | boolean found = false; 69 | 70 | for (StackTraceElement trace : traces) { 71 | try { 72 | if (found) { 73 | if (!trace.getClassName().startsWith(className)) { 74 | Class clazz = Class.forName(trace.getClassName()); 75 | return "[" + getClassName(clazz) + ":" 76 | + trace.getMethodName() + ":" 77 | + trace.getLineNumber() + "]: "; 78 | } 79 | } else if (trace.getClassName().startsWith(className)) { 80 | found = true; 81 | } 82 | } catch (ClassNotFoundException ignored) { 83 | } 84 | } 85 | 86 | return "[]: "; 87 | } 88 | 89 | private static String getClassName(Class clazz) { 90 | if (clazz != null) { 91 | if (!TextUtils.isEmpty(clazz.getSimpleName())) { 92 | return clazz.getSimpleName(); 93 | } 94 | 95 | return getClassName(clazz.getEnclosingClass()); 96 | } 97 | 98 | return ""; 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /app/src/main/java/com/handmall/tabbar/widget/PullToRefresh/util/Utils.java: -------------------------------------------------------------------------------- 1 | package com.handmall.tabbar.widget.PullToRefresh.util; 2 | 3 | import android.content.Context; 4 | 5 | public class Utils { 6 | 7 | public static int convertDpToPixel(Context context, int dp) { 8 | float density = context.getResources().getDisplayMetrics().density; 9 | return Math.round((float) dp * density); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/buildings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/drawable-hdpi/buildings.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/icon_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/drawable-hdpi/icon_1.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/icon_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/drawable-hdpi/icon_2.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/icon_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/drawable-hdpi/icon_3.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/lib_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/drawable-hdpi/lib_icon.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/sky.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/drawable-hdpi/sky.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/sun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/drawable-hdpi/sun.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/buildings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/drawable-mdpi/buildings.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/icon_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/drawable-mdpi/icon_1.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/icon_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/drawable-mdpi/icon_2.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/icon_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/drawable-mdpi/icon_3.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/lib_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/drawable-mdpi/lib_icon.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/sky.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/drawable-mdpi/sky.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/sun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/drawable-mdpi/sun.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/buildings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/drawable-xhdpi/buildings.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/icon_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/drawable-xhdpi/icon_1.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/icon_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/drawable-xhdpi/icon_2.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/icon_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/drawable-xhdpi/icon_3.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/lib_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/drawable-xhdpi/lib_icon.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/sky.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/drawable-xhdpi/sky.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/sun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/drawable-xhdpi/sun.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/buildings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/drawable-xxhdpi/buildings.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/icon_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/drawable-xxhdpi/icon_1.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/icon_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/drawable-xxhdpi/icon_2.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/icon_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/drawable-xxhdpi/icon_3.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/lib_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/drawable-xxhdpi/lib_icon.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/sky.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/drawable-xxhdpi/sky.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/sun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/drawable-xxhdpi/sun.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/buildings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/drawable-xxxhdpi/buildings.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/icon_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/drawable-xxxhdpi/icon_1.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/icon_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/drawable-xxxhdpi/icon_2.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/icon_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/drawable-xxxhdpi/icon_3.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/lib_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/drawable-xxxhdpi/lib_icon.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/sky.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/drawable-xxxhdpi/sky.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/sun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/drawable-xxxhdpi/sun.png -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 17 | 18 | 19 | 25 | 26 | 33 | 34 | 41 | 42 | 43 | 49 | 50 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_pull_to_refresh.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | 17 | 18 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/layout/comui_tab_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 14 | 15 | 19 | 20 | 21 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_city.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_home.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_list_view.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_message.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_person.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_recycler_view.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 9 | 10 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/layout/list_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/comui_bar_top_shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/mipmap-xhdpi/comui_bar_top_shadow.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/comui_tab_city.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/mipmap-xhdpi/comui_tab_city.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/comui_tab_city_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/mipmap-xhdpi/comui_tab_city_selected.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/comui_tab_find.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/mipmap-xhdpi/comui_tab_find.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/comui_tab_find_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/mipmap-xhdpi/comui_tab_find_selected.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/comui_tab_home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/mipmap-xhdpi/comui_tab_home.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/comui_tab_home_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/mipmap-xhdpi/comui_tab_home_selected.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/comui_tab_message.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/mipmap-xhdpi/comui_tab_message.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/comui_tab_message_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/mipmap-xhdpi/comui_tab_message_selected.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/comui_tab_person.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/mipmap-xhdpi/comui_tab_person.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/comui_tab_person_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/mipmap-xhdpi/comui_tab_person_selected.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/comui_tab_post.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/mipmap-xhdpi/comui_tab_post.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/attr.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | #ff333333 8 | #212121 9 | 10 | #F4B63E 11 | #663D4E 12 | #EB6460 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | 4.0dip 7 | 8 | 222dp 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | tabbar 3 | bar-shadow 4 | tab-post 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/test/java/com/handmall/tabbar/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.handmall.tabbar; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:2.2.1' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | jcenter() 18 | } 19 | } 20 | 21 | task clean(type: Delete) { 22 | delete rootProject.buildDir 23 | } 24 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /screenshots/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudeing/tabbar/22ff9cf3d14c1a4077624c63e1940211b3c868b8/screenshots/1.png -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | --------------------------------------------------------------------------------