diff --git a/app/build.gradle b/app/build.gradle index 7f7a889..c9e614e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -166,7 +166,7 @@ android { // } oss { applicationId "com.xuebiping.bolizhuzi" - buildConfigField "boolean", "IS_DEV", "false" + buildConfigField "boolean", "IS_DEV", "true" buildConfigField "String", "PRIVACY", '"/index/about/siyuPrivacy.html"' buildConfigField "String", "AGREEMENT", '"/index/about/siyuAgreement.html"' buildConfigField "String", "VIP_AGREEMENT", '"/index/about/siyuVipAgreement.html"' @@ -348,6 +348,7 @@ android { implementation project(path: ':storage') implementation project(path: ':yunxinkit') implementation project(':faceunity') + implementation project(':magicindicator') //EventBus implementation 'de.greenrobot:eventbus:2.4.0' implementation 'com.tencent.mm.opensdk:wechat-sdk-android-without-mta:6.8.0' @@ -398,6 +399,9 @@ android { implementation 'com.devzld:expandlayout:1.0.0' implementation 'io.github.lucksiege:pictureselector:v3.11.2' implementation 'com.github.Dimezis:BlurView:version-3.1.0' + + implementation "com.scwang.smartrefresh:SmartRefreshLayout:1.1.0-x" + implementation "com.github.CymChad:BaseRecyclerViewAdapterHelper:3.0.6" //3.0.2更换了很多API } } diff --git a/app/oss/release/output-metadata.json b/app/oss/release/output-metadata.json new file mode 100644 index 0000000..3f847dd --- /dev/null +++ b/app/oss/release/output-metadata.json @@ -0,0 +1,18 @@ +{ + "version": 2, + "artifactType": { + "type": "APK", + "kind": "Directory" + }, + "applicationId": "com.xuebiping.bolizhuzi", + "variantName": "ossRelease", + "elements": [ + { + "type": "SINGLE", + "filters": [], + "versionCode": 1, + "versionName": "1.0.0", + "outputFile": "siyu_v1.0.0_oss_release.apk" + } + ] +} \ No newline at end of file diff --git a/app/oss/release/siyu_v1.0.0_oss_release.apk b/app/oss/release/siyu_v1.0.0_oss_release.apk new file mode 100644 index 0000000..ad232cd Binary files /dev/null and b/app/oss/release/siyu_v1.0.0_oss_release.apk differ diff --git a/app/src/main/java/com/xuebiping/bolizhuzi/controller/dynamics/adapter/DynamicsAdapter.java b/app/src/main/java/com/xuebiping/bolizhuzi/controller/dynamics/adapter/DynamicsAdapter.java index b063384..a0422c0 100644 --- a/app/src/main/java/com/xuebiping/bolizhuzi/controller/dynamics/adapter/DynamicsAdapter.java +++ b/app/src/main/java/com/xuebiping/bolizhuzi/controller/dynamics/adapter/DynamicsAdapter.java @@ -136,7 +136,7 @@ public class DynamicsAdapter extends BaseAdapter { public View getView(int i, View view, ViewGroup viewGroup) { Holder holder = null; if (null == view) { - view = View.inflate(mContext, R.layout.item_dynamic_list1, null); + view = View.inflate(mContext, R.layout.item_dynamic_list, null); holder = initView(view); } else { holder = (Holder) view.getTag(R.id.tag_second); @@ -155,7 +155,7 @@ public class DynamicsAdapter extends BaseAdapter { holder.newPeopleImageView.setVisibility(View.GONE); holder.realPeopleImageView.setVisibility(View.GONE); - holder.tv_see_count.setText("已有"+bean.getLike_count()+"次浏览"); +// holder.tv_see_count.setText("已有"+bean.getLike_count()+"次浏览"); if (bean.getGender() == 1) { holder.realPeopleImageView.setVisibility(View.VISIBLE); diff --git a/app/src/main/java/com/xuebiping/bolizhuzi/controller/main/adapter/MainLookMeAdapter.java b/app/src/main/java/com/xuebiping/bolizhuzi/controller/main/adapter/MainLookMeAdapter.java new file mode 100644 index 0000000..3541d5f --- /dev/null +++ b/app/src/main/java/com/xuebiping/bolizhuzi/controller/main/adapter/MainLookMeAdapter.java @@ -0,0 +1,29 @@ +package com.xuebiping.bolizhuzi.controller.main.adapter; + +import android.content.Context; + +import com.chad.library.adapter.base.BaseQuickAdapter; +import com.chad.library.adapter.base.viewholder.BaseViewHolder; +import com.facebook.drawee.view.SimpleDraweeView; +import com.xuebiping.bolizhuzi.R; +import com.xuebiping.bolizhuzi.model.main.LookMeBean; +import com.xuebiping.bolizhuzi.utils.StrU; + +import org.jetbrains.annotations.NotNull; + +public class MainLookMeAdapter extends BaseQuickAdapter { + + private Context mContext; + + public MainLookMeAdapter(int layoutResId, Context context) { + super(layoutResId); + this.mContext = context; + } + + @Override + protected void convert(@NotNull BaseViewHolder baseViewHolder, LookMeBean lookMeBean) { + SimpleDraweeView list_photo = baseViewHolder.getView(R.id.list_photo); + + list_photo.setImageURI(StrU.getResourcePath(lookMeBean.getAvatar_url(), mContext)); + } +} diff --git a/app/src/main/java/com/xuebiping/bolizhuzi/controller/main/adapter/ScaleTransitionPagerTitleView.java b/app/src/main/java/com/xuebiping/bolizhuzi/controller/main/adapter/ScaleTransitionPagerTitleView.java new file mode 100644 index 0000000..1ffb4bb --- /dev/null +++ b/app/src/main/java/com/xuebiping/bolizhuzi/controller/main/adapter/ScaleTransitionPagerTitleView.java @@ -0,0 +1,40 @@ +package com.xuebiping.bolizhuzi.controller.main.adapter; + +import android.content.Context; + +import net.lucode.hackware.magicindicator.buildins.commonnavigator.titles.ColorTransitionPagerTitleView; + +/** + * 带颜色渐变和缩放的指示器标题 + * 博客: http://hackware.lucode.net + * Created by hackware on 2016/6/26. + */ +public class ScaleTransitionPagerTitleView extends ColorTransitionPagerTitleView { + private float mMinScale = 0.75f; + + public ScaleTransitionPagerTitleView(Context context) { + super(context); + } + + @Override + public void onEnter(int index, int totalCount, float enterPercent, boolean leftToRight) { + super.onEnter(index, totalCount, enterPercent, leftToRight); // 实现颜色渐变 + setScaleX(mMinScale + (1.0f - mMinScale) * enterPercent); + setScaleY(mMinScale + (1.0f - mMinScale) * enterPercent); + } + + @Override + public void onLeave(int index, int totalCount, float leavePercent, boolean leftToRight) { + super.onLeave(index, totalCount, leavePercent, leftToRight); // 实现颜色渐变 + setScaleX(1.0f + (mMinScale - 1.0f) * leavePercent); + setScaleY(1.0f + (mMinScale - 1.0f) * leavePercent); + } + + public float getMinScale() { + return mMinScale; + } + + public void setMinScale(float minScale) { + mMinScale = minScale; + } +} diff --git a/app/src/main/java/com/xuebiping/bolizhuzi/model/main/LookMeBean.java b/app/src/main/java/com/xuebiping/bolizhuzi/model/main/LookMeBean.java new file mode 100644 index 0000000..6e53f8e --- /dev/null +++ b/app/src/main/java/com/xuebiping/bolizhuzi/model/main/LookMeBean.java @@ -0,0 +1,23 @@ +package com.xuebiping.bolizhuzi.model.main; + +public class LookMeBean { + + private String id; + private String avatar_url; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getAvatar_url() { + return avatar_url; + } + + public void setAvatar_url(String avatar_url) { + this.avatar_url = avatar_url; + } +} diff --git a/app/src/main/java/com/xuebiping/bolizhuzi/model/main/LookMeListBean.java b/app/src/main/java/com/xuebiping/bolizhuzi/model/main/LookMeListBean.java new file mode 100644 index 0000000..c2a1097 --- /dev/null +++ b/app/src/main/java/com/xuebiping/bolizhuzi/model/main/LookMeListBean.java @@ -0,0 +1,17 @@ +package com.xuebiping.bolizhuzi.model.main; + +import java.util.ArrayList; +import java.util.List; + +public class LookMeListBean { + + private List list = new ArrayList<>(); + + public List getList() { + return list; + } + + public void setList(List list) { + this.list = list; + } +} diff --git a/app/src/main/java/com/xuebiping/bolizhuzi/view/dynamics/activity/DynamicDetailTwoActivity.java b/app/src/main/java/com/xuebiping/bolizhuzi/view/dynamics/activity/DynamicDetailTwoActivity.java index 34b1a10..cee2285 100644 --- a/app/src/main/java/com/xuebiping/bolizhuzi/view/dynamics/activity/DynamicDetailTwoActivity.java +++ b/app/src/main/java/com/xuebiping/bolizhuzi/view/dynamics/activity/DynamicDetailTwoActivity.java @@ -31,7 +31,7 @@ public class DynamicDetailTwoActivity extends BaseActivity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_dynamic_detail_two); - bean = getIntent().getParcelableExtra("bean"); + bean = (DynamicsItemBean) getIntent().getSerializableExtra("bean"); mImageList.add(bean); setTitleName("动态详情"); diff --git a/app/src/main/java/com/xuebiping/bolizhuzi/view/dynamics/fragment/RecentDynamicFragment.java b/app/src/main/java/com/xuebiping/bolizhuzi/view/dynamics/fragment/RecentDynamicFragment.java index f8596c8..64fe00e 100644 --- a/app/src/main/java/com/xuebiping/bolizhuzi/view/dynamics/fragment/RecentDynamicFragment.java +++ b/app/src/main/java/com/xuebiping/bolizhuzi/view/dynamics/fragment/RecentDynamicFragment.java @@ -178,15 +178,15 @@ public class RecentDynamicFragment extends Fragment { @Override public void onDetailClicked(int position, String id) { detailPosition = position; - /*Intent intent = new Intent(getContext(), DynamicDetailActivity.class); + Intent intent = new Intent(getContext(), DynamicDetailActivity.class); intent.putExtra("dynamic_id", id); intent.putExtra("dynamic_from", 0); - startActivity(intent);*/ - - DynamicsItemBean itemBean = mAdapter.getmDynamicsItemList().get(position); - Intent intent = new Intent(getContext(), DynamicDetailTwoActivity.class); - intent.putExtra("bean", (Parcelable) itemBean); startActivity(intent); + +// DynamicsItemBean itemBean = mAdapter.getmDynamicsItemList().get(position); +// Intent intent = new Intent(getContext(), DynamicDetailTwoActivity.class); +// intent.putExtra("bean", itemBean); +// startActivity(intent); } }); diff --git a/app/src/main/java/com/xuebiping/bolizhuzi/view/main/LookMeFragment.java b/app/src/main/java/com/xuebiping/bolizhuzi/view/main/LookMeFragment.java new file mode 100644 index 0000000..8532622 --- /dev/null +++ b/app/src/main/java/com/xuebiping/bolizhuzi/view/main/LookMeFragment.java @@ -0,0 +1,89 @@ +package com.xuebiping.bolizhuzi.view.main; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.scwang.smartrefresh.layout.SmartRefreshLayout; +import com.scwang.smartrefresh.layout.api.RefreshLayout; +import com.scwang.smartrefresh.layout.listener.OnRefreshLoadMoreListener; +import com.xuebiping.bolizhuzi.R; +import com.xuebiping.bolizhuzi.controller.main.adapter.MainLookMeAdapter; +import com.xuebiping.bolizhuzi.model.main.LookMeBean; + +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +public class LookMeFragment extends Fragment implements OnRefreshLoadMoreListener { + + private View v; + + private SmartRefreshLayout mSmart_refresh; + private RecyclerView mRv_recommend_list; + private MainLookMeAdapter mMainLookMeAdapter; + + private int mPage = 1; + private List mData = new ArrayList<>(); + + @Nullable + @org.jetbrains.annotations.Nullable + @Override + public View onCreateView(@NonNull @NotNull LayoutInflater inflater, @Nullable @org.jetbrains.annotations.Nullable ViewGroup container, @Nullable @org.jetbrains.annotations.Nullable Bundle savedInstanceState) { + v = inflater.inflate(R.layout.fragment_look_me, container, false); + initView(v); + getData(mPage); + return v; + } + + private void initView(View v) { + mSmart_refresh = v.findViewById(R.id.smart_refresh); + mRv_recommend_list = v.findViewById(R.id.rv_recommend_list); + mRv_recommend_list.setLayoutManager(new LinearLayoutManager(getActivity())); + mMainLookMeAdapter = new MainLookMeAdapter(R.layout.item_main_look_me, getActivity()); + mRv_recommend_list.setAdapter(mMainLookMeAdapter); + + mSmart_refresh.setOnRefreshLoadMoreListener(this); + } + + private void getData(int page) { + + for (int i = 0; i < 10; i++) { + + LookMeBean lookMeBean = new LookMeBean(); + lookMeBean.setId(i + ""); + lookMeBean.setAvatar_url("uploads/admin/202403/25/04cf409ab9148f11c0052bb254b5c155.jpg"); + mData.add(lookMeBean); + + } + + finishFresh(); + mMainLookMeAdapter.setList(mData); + + } + + public void finishFresh() { + mSmart_refresh.finishLoadMore(); + mSmart_refresh.finishRefresh(); + } + + @Override + public void onLoadMore(@NonNull @NotNull RefreshLayout refreshLayout) { + mPage++; + getData(mPage); + } + + @Override + public void onRefresh(@NonNull @NotNull RefreshLayout refreshLayout) { + mPage = 1; + getData(mPage); + } +} diff --git a/app/src/main/java/com/xuebiping/bolizhuzi/view/main/MainActivity.java b/app/src/main/java/com/xuebiping/bolizhuzi/view/main/MainActivity.java index 68539f9..7448e86 100644 --- a/app/src/main/java/com/xuebiping/bolizhuzi/view/main/MainActivity.java +++ b/app/src/main/java/com/xuebiping/bolizhuzi/view/main/MainActivity.java @@ -168,7 +168,7 @@ public class MainActivity extends BaseActivity implements DemoHelper.AppIdsUpdat private LuckFragment luckFragment; private SwipeCardFragment swipeCardFragment; private DynamicViewPagerFragment mDynamicViewPagerFragment; - private MainRecommendFragment mMainRecommendFragment; + private MainRecommend2Fragment mMainRecommendFragment; private MainMessageFragment mMainMessageFragment; private SettingFragment mSettingFragment; private Disposable mDisposable; @@ -280,27 +280,27 @@ public class MainActivity extends BaseActivity implements DemoHelper.AppIdsUpdat } } - @Override - protected void onNewIntent(Intent intent) { - super.onNewIntent(intent); - boolean isFormTask = intent.getBooleanExtra("isFormTask", false); - boolean isMain = intent.getBooleanExtra("isMain", false); - if (isFormTask) { - mBottomTab.change(1); - } - if (isMain) { - int marketStatus = NoClearSPUtils.getInt(this, Constans.MARKET_STATUS, 0); - if (marketStatus == 0 || SPUtils.getInt(this, ConsUser.TYPE) == 2) { - mBottomTab.change(0); - } else { - if (UserManager.getUserInfo().isWomen()) { - mBottomTab.change(0); - } else { - mBottomTab.change(2); - } - } - } - } +// @Override +// protected void onNewIntent(Intent intent) { +// super.onNewIntent(intent); +// boolean isFormTask = intent.getBooleanExtra("isFormTask", false); +// boolean isMain = intent.getBooleanExtra("isMain", false); +// if (isFormTask) { +// mBottomTab.change(1); +// } +// if (isMain) { +// int marketStatus = NoClearSPUtils.getInt(this, Constans.MARKET_STATUS, 0); +// if (marketStatus == 0 || SPUtils.getInt(this, ConsUser.TYPE) == 2) { +// mBottomTab.change(0); +// } else { +// if (UserManager.getUserInfo().isWomen()) { +// mBottomTab.change(0); +// } else { +// mBottomTab.change(2); +// } +// } +// } +// } /** * 调起使用这个会弹出hms 下载框 @@ -876,9 +876,9 @@ public class MainActivity extends BaseActivity implements DemoHelper.AppIdsUpdat if (position == currentPosition) { //第二次点击 if (position == 0) { if (isOne) { - if (null != mMainRecommendFragment) { - mMainRecommendFragment.clickRefresh(); - } +// if (null != mMainRecommendFragment) { +// mMainRecommendFragment.clickRefresh(); +// } } isOne = true; } else if (position == 1) { @@ -888,9 +888,9 @@ public class MainActivity extends BaseActivity implements DemoHelper.AppIdsUpdat } } else if (position == 0) { //重新点击回来,检测版本更新 checkUpdate(); - if (null != mMainRecommendFragment) { - mMainRecommendFragment.RefreshFragment(); - } +// if (null != mMainRecommendFragment) { +// mMainRecommendFragment.RefreshFragment(); +// } } else if (position == 3) { // MaleToast.showMessage(MainActivity.this,"点击了消息"); getGreet(); @@ -1060,7 +1060,7 @@ public class MainActivity extends BaseActivity implements DemoHelper.AppIdsUpdat mDynamicViewPagerFragment = new DynamicViewPagerFragment(); } if (mMainRecommendFragment == null) { - mMainRecommendFragment = new MainRecommendFragment(); + mMainRecommendFragment = new MainRecommend2Fragment(); } if (mMainMessageFragment == null) { mMainMessageFragment = new MainMessageFragment(); @@ -1069,24 +1069,25 @@ public class MainActivity extends BaseActivity implements DemoHelper.AppIdsUpdat mSettingFragment = new SettingFragment(); } + mBottomTab.addTab(mMainRecommendFragment); + mBottomTab.addTab(mDynamicViewPagerFragment); if (marketStatus == 0 || SPUtils.getInt(this, ConsUser.TYPE) == 2) { mBottomTab.addTab(swipeCardFragment); } else { mBottomTab.addTab(luckFragment); } - mBottomTab.addTab(mDynamicViewPagerFragment); - mBottomTab.addTab(mMainRecommendFragment); mBottomTab.addTab(mMainMessageFragment); mBottomTab.addTab(mSettingFragment); - if (marketStatus == 0 || SPUtils.getInt(this, ConsUser.TYPE) == 2) { - mBottomTab.change(0); - } else { - if (UserManager.getUserInfo().isWomen()) { - mBottomTab.change(0); - } else { - mBottomTab.change(2); - } - } + mBottomTab.change(0); +// if (marketStatus == 0 || SPUtils.getInt(this, ConsUser.TYPE) == 2) { +// mBottomTab.change(0); +// } else { +// if (UserManager.getUserInfo().isWomen()) { +// mBottomTab.change(0); +// } else { +// mBottomTab.change(2); +// } +// } } // // @Override diff --git a/app/src/main/java/com/xuebiping/bolizhuzi/view/main/MainRecommend2Fragment.java b/app/src/main/java/com/xuebiping/bolizhuzi/view/main/MainRecommend2Fragment.java new file mode 100644 index 0000000..67f9942 --- /dev/null +++ b/app/src/main/java/com/xuebiping/bolizhuzi/view/main/MainRecommend2Fragment.java @@ -0,0 +1,267 @@ +package com.xuebiping.bolizhuzi.view.main; + +import android.content.Context; +import android.content.Intent; +import android.graphics.Color; +import android.graphics.Typeface; +import android.os.Bundle; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.DecelerateInterpolator; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import androidx.fragment.app.Fragment; +import androidx.viewpager.widget.ViewPager; + +import com.fengliyan.uikit.UiUtils; +import com.fengliyan.uikit.toast.MaleToast; +import com.xuebiping.bolizhuzi.R; +import com.xuebiping.bolizhuzi.controller.main.adapter.MainViewPagerAdapter; +import com.xuebiping.bolizhuzi.controller.main.adapter.ScaleTransitionPagerTitleView; + +import net.lucode.hackware.magicindicator.MagicIndicator; +import net.lucode.hackware.magicindicator.ViewPagerHelper; +import net.lucode.hackware.magicindicator.buildins.UIUtil; +import net.lucode.hackware.magicindicator.buildins.commonnavigator.CommonNavigator; +import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.CommonNavigatorAdapter; +import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IPagerIndicator; +import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IPagerTitleView; +import net.lucode.hackware.magicindicator.buildins.commonnavigator.indicators.LinePagerIndicator; +import net.lucode.hackware.magicindicator.buildins.commonnavigator.titles.CommonPagerTitleView; +import net.lucode.hackware.magicindicator.buildins.commonnavigator.titles.SimplePagerTitleView; + +import java.util.ArrayList; +import java.util.List; + +public class MainRecommend2Fragment extends Fragment implements View.OnClickListener { + + private View v; + private RelativeLayout mRl_home_search; + private LinearLayout mLl_select; + private MagicIndicator mMagicIndicator; + private ViewPager mMainViewPager; + + private MainViewPagerAdapter mViewPagerAdapter; + private List mChannelFragments = new ArrayList<>(); + private List mDataList = new ArrayList<>(); + private int mIndex = 1; + + private LookMeFragment mLookMeFragment; + private RecommendFragment1 mLookMeFragment1; + private DefaultRecommendFragment mLookMeFragment2; + private MainRecommendActiveFragment mLookMeFragment3; + private MainRecommendPeopleFragment mLookMeFragment4; + private MainRecommendCityFragment mLookMeFragment5; + private LookMeFragment mLookMeFragment6; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + v = inflater.inflate(R.layout.fragment_main_recommend2, container, false); + initView(v); + return v; + } + + + private void initView(View v) { + mLl_select = v.findViewById(R.id.ll_select); + mRl_home_search = v.findViewById(R.id.rl_home_search); + mMagicIndicator = v.findViewById(R.id.magic_indicator); + mMainViewPager = v.findViewById(R.id.main_view_pager); + + mLl_select.setOnClickListener(this); + mRl_home_search.setOnClickListener(this); + + mLookMeFragment = new LookMeFragment(); + mLookMeFragment1 = new RecommendFragment1(); + mLookMeFragment2 = new DefaultRecommendFragment(); + mLookMeFragment3 = new MainRecommendActiveFragment(); + mLookMeFragment4 = new MainRecommendPeopleFragment(); + mLookMeFragment5 = new MainRecommendCityFragment(); + mLookMeFragment6 = new LookMeFragment(); + + mChannelFragments.add(mLookMeFragment); + mChannelFragments.add(mLookMeFragment1); + mChannelFragments.add(mLookMeFragment2); + mChannelFragments.add(mLookMeFragment3); + mChannelFragments.add(mLookMeFragment4); + mChannelFragments.add(mLookMeFragment5); + mChannelFragments.add(mLookMeFragment6); + + mViewPagerAdapter = new MainViewPagerAdapter(getChildFragmentManager(), + mChannelFragments); + mMainViewPager.setAdapter(mViewPagerAdapter); + + initMagicIndicator(); + + mMainViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + + } + + @Override + public void onPageSelected(int position) { +// if (position == 3) { +// isReClick = true; +// mIv_more_city.setVisibility(View.VISIBLE); +// } else { +// isReClick = false; +// mIv_more_city.setVisibility(View.INVISIBLE); +// } +// + mIndex = position; + } + + @Override + public void onPageScrollStateChanged(int state) { + + } + }); + + mViewPagerAdapter.notifyDataSetChanged(); + mMainViewPager.setCurrentItem(mIndex); + } + + private void initMagicIndicator() { + mDataList.clear(); + mDataList.add("看过我"); + mDataList.add("推荐"); + mDataList.add("语聊"); + mDataList.add("新人"); + mDataList.add("五星"); + mDataList.add("四星"); + mDataList.add("三星"); + + CommonNavigator commonNavigator = new CommonNavigator(getActivity()); + commonNavigator.setAdapter(new CommonNavigatorAdapter() { + @Override + public int getCount() { + return mDataList == null ? 0 : mDataList.size(); + } + + @Override + public IPagerTitleView getTitleView(Context context, final int index) { + SimplePagerTitleView simplePagerTitleView = new ScaleTransitionPagerTitleView(context); + simplePagerTitleView.setText(mDataList.get(index)); + simplePagerTitleView.setTextSize(20); + simplePagerTitleView.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD));//加粗 + simplePagerTitleView.setNormalColor(Color.parseColor("#33000000")); + simplePagerTitleView.setSelectedColor(Color.parseColor("#000000")); + simplePagerTitleView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { +// if (isReClick && index == 3) { +// Intent intent = new Intent(getActivity(), CityPickerActivity.class); +// startActivityForResult(intent, RECOMMEND_CHANGE_CITY_REQUEST); +// } else { +// mMainViewPager.setCurrentItem(index); +// } + Log.d("nail", "onClick: " + index); + mMainViewPager.setCurrentItem(index); + + } + }); + return simplePagerTitleView; + +// CommonPagerTitleView commonPagerTitleView = new CommonPagerTitleView(getActivity()); +// commonPagerTitleView.setContentView(R.layout.main_pager_title_layout); +// +// // 初始化 +// ImageView iv_selected = (ImageView) commonPagerTitleView.findViewById(R.id.iv_selected); +// ImageView iv_no_selected = (ImageView) commonPagerTitleView.findViewById(R.id.iv_no_selected); +// final TextView titleText = (TextView) commonPagerTitleView.findViewById(R.id.title_text); +// titleText.setText(mDataList.get(index)); +// commonPagerTitleView.setPadding(UIUtil.dip2px(context, 5), +// UIUtil.dip2px(context, 0), +// UIUtil.dip2px(context, 5), +// UIUtil.dip2px(context, 0)); +// +// commonPagerTitleView.setOnPagerTitleChangeListener(new CommonPagerTitleView.OnPagerTitleChangeListener() { +// +// @Override +// public void onSelected(int index, int totalCount) { +// titleText.setTextSize(16); +// titleText.setTextColor(Color.parseColor("#ffffff")); +// titleText.setTypeface(Typeface.DEFAULT_BOLD); +// iv_selected.setVisibility(View.VISIBLE); +// iv_no_selected.setVisibility(View.INVISIBLE); +// } +// +// @Override +// public void onDeselected(int index, int totalCount) { +// titleText.setTextSize(16); +// titleText.setTextColor(Color.parseColor("#33000000")); +// titleText.setTypeface(Typeface.DEFAULT); +// iv_selected.setVisibility(View.INVISIBLE); +// iv_no_selected.setVisibility(View.VISIBLE); +// } +// +// @Override +// public void onLeave(int index, int totalCount, float leavePercent, boolean leftToRight) { +// // rl_title_bg.setScaleX(1.3f + (0.8f - 1.3f) * leavePercent); +// //rl_title_bg.setScaleY(1.3f + (0.8f - 1.3f) * leavePercent); +// } +// +// @Override +// public void onEnter(int index, int totalCount, float enterPercent, boolean leftToRight) { +// // rl_title_bg.setScaleX(0.8f + (1.3f - 0.8f) * enterPercent); +// // rl_title_bg.setScaleY(0.8f + (1.3f - 0.8f) * enterPercent); +// } +// }); +// +// commonPagerTitleView.setOnClickListener(new View.OnClickListener() { +// @Override +// public void onClick(View v) { +// if (isReClick && index == 3) { +// Intent intent = new Intent(getActivity(), CityPickerActivity.class); +// startActivityForResult(intent, RECOMMEND_CHANGE_CITY_REQUEST); +// } else { +// mMainViewPager.setCurrentItem(index); +// } +// mMainViewPager.setCurrentItem(index); +// } +// }); +// +// return commonPagerTitleView; + } + + @Override + public IPagerIndicator getIndicator(Context context) { +// LinePagerIndicator indicator = new LinePagerIndicator(context); +// indicator.setMode(LinePagerIndicator.MODE_WRAP_CONTENT); +// indicator.setLineHeight(UIUtil.dip2px(context, 10)); +// indicator.setLineWidth(UIUtil.dip2px(context, 20)); +// indicator.setRoundRadius(UIUtil.dip2px(context, 0)); +// indicator.setStartInterpolator(new AccelerateInterpolator()); +// indicator.setEndInterpolator(new DecelerateInterpolator(2.0f)); +// indicator.setColors(getResources().getColor(R.color.yellow_ffd33e)); +// indicator.setYOffset(UIUtil.dip2px(context, 10)); +// indicator.setXOffset(0); + + LinePagerIndicator indicator = new LinePagerIndicator(context); + indicator.setColors(getResources().getColor(R.color.yellow_ffd33e)); + indicator.setLineHeight(UIUtil.dip2px(context, 10)); + indicator.setMode(LinePagerIndicator.MODE_WRAP_CONTENT); + return null; + } + }); + mMagicIndicator.setNavigator(commonNavigator); + ViewPagerHelper.bind(mMagicIndicator, mMainViewPager); + } + + @Override + public void onClick(View view) { + if (view == mRl_home_search) { + MaleToast.showMessage(getActivity(), "搜索"); + }else if (view == mLl_select) { + MaleToast.showMessage(getActivity(), "筛选"); + } + } +} diff --git a/app/src/main/res/drawable-xxhdpi/ic_main_select_man_and_woman.png b/app/src/main/res/drawable-xxhdpi/ic_main_select_man_and_woman.png new file mode 100644 index 0000000..b7f117e Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_main_select_man_and_woman.png differ diff --git a/app/src/main/res/drawable/look_me_online_bg.xml b/app/src/main/res/drawable/look_me_online_bg.xml new file mode 100644 index 0000000..75a6389 --- /dev/null +++ b/app/src/main/res/drawable/look_me_online_bg.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_look_me.xml b/app/src/main/res/layout/fragment_look_me.xml new file mode 100644 index 0000000..8293a0d --- /dev/null +++ b/app/src/main/res/layout/fragment_look_me.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_main_recommend2.xml b/app/src/main/res/layout/fragment_main_recommend2.xml new file mode 100644 index 0000000..a50d898 --- /dev/null +++ b/app/src/main/res/layout/fragment_main_recommend2.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_main_look_me.xml b/app/src/main/res/layout/item_main_look_me.xml new file mode 100644 index 0000000..d1c69d7 --- /dev/null +++ b/app/src/main/res/layout/item_main_look_me.xml @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-xxhdpi/icon_img_no_black.png b/app/src/main/res/mipmap-xxhdpi/icon_img_no_black.png new file mode 100644 index 0000000..fa97710 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/icon_img_no_black.png differ diff --git a/magicindicator/build.gradle b/magicindicator/build.gradle new file mode 100644 index 0000000..5133208 --- /dev/null +++ b/magicindicator/build.gradle @@ -0,0 +1,20 @@ +apply plugin: 'com.android.library' + +android { + compileSdkVersion 28 + + defaultConfig { + minSdkVersion 21 + targetSdkVersion 28 + } + buildTypes { + release { + minifyEnabled false + } + } +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation 'com.android.support:appcompat-v7:28.0.0' +} \ No newline at end of file diff --git a/magicindicator/src/main/AndroidManifest.xml b/magicindicator/src/main/AndroidManifest.xml new file mode 100644 index 0000000..3949813 --- /dev/null +++ b/magicindicator/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + diff --git a/magicindicator/src/main/java/net/lucode/hackware/magicindicator/FragmentContainerHelper.java b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/FragmentContainerHelper.java new file mode 100644 index 0000000..29eb741 --- /dev/null +++ b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/FragmentContainerHelper.java @@ -0,0 +1,162 @@ +package net.lucode.hackware.magicindicator; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.annotation.TargetApi; +import android.os.Build; +import android.view.animation.AccelerateDecelerateInterpolator; +import android.view.animation.Interpolator; + +import net.lucode.hackware.magicindicator.buildins.commonnavigator.model.PositionData; + +import java.util.ArrayList; +import java.util.List; + + +/** + * 使得MagicIndicator在FragmentContainer中使用 + * Created by hackware on 2016/9/4. + */ + +@TargetApi(Build.VERSION_CODES.HONEYCOMB) +public class FragmentContainerHelper { + private List mMagicIndicators = new ArrayList(); + private ValueAnimator mScrollAnimator; + private int mLastSelectedIndex; + private int mDuration = 150; + private Interpolator mInterpolator = new AccelerateDecelerateInterpolator(); + + private Animator.AnimatorListener mAnimatorListener = new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + dispatchPageScrollStateChanged(ScrollState.SCROLL_STATE_IDLE); + mScrollAnimator = null; + } + }; + + private ValueAnimator.AnimatorUpdateListener mAnimatorUpdateListener = new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + float positionOffsetSum = (Float) animation.getAnimatedValue(); + int position = (int) positionOffsetSum; + float positionOffset = positionOffsetSum - position; + if (positionOffsetSum < 0) { + position = position - 1; + positionOffset = 1.0f + positionOffset; + } + dispatchPageScrolled(position, positionOffset, 0); + } + }; + + public FragmentContainerHelper() { + } + + public FragmentContainerHelper(MagicIndicator magicIndicator) { + mMagicIndicators.add(magicIndicator); + } + + /** + * IPagerIndicator支持弹性效果的辅助方法 + * + * @param positionDataList + * @param index + * @return + */ + public static PositionData getImitativePositionData(List positionDataList, int index) { + if (index >= 0 && index <= positionDataList.size() - 1) { // 越界后,返回假的PositionData + return positionDataList.get(index); + } else { + PositionData result = new PositionData(); + PositionData referenceData; + int offset; + if (index < 0) { + offset = index; + referenceData = positionDataList.get(0); + } else { + offset = index - positionDataList.size() + 1; + referenceData = positionDataList.get(positionDataList.size() - 1); + } + result.mLeft = referenceData.mLeft + offset * referenceData.width(); + result.mTop = referenceData.mTop; + result.mRight = referenceData.mRight + offset * referenceData.width(); + result.mBottom = referenceData.mBottom; + result.mContentLeft = referenceData.mContentLeft + offset * referenceData.width(); + result.mContentTop = referenceData.mContentTop; + result.mContentRight = referenceData.mContentRight + offset * referenceData.width(); + result.mContentBottom = referenceData.mContentBottom; + return result; + } + } + + public void handlePageSelected(int selectedIndex) { + handlePageSelected(selectedIndex, true); + } + + public void handlePageSelected(int selectedIndex, boolean smooth) { + if (mLastSelectedIndex == selectedIndex) { + return; + } + if (smooth) { + if (mScrollAnimator == null || !mScrollAnimator.isRunning()) { + dispatchPageScrollStateChanged(ScrollState.SCROLL_STATE_SETTLING); + } + dispatchPageSelected(selectedIndex); + float currentPositionOffsetSum = mLastSelectedIndex; + if (mScrollAnimator != null) { + currentPositionOffsetSum = (Float) mScrollAnimator.getAnimatedValue(); + mScrollAnimator.cancel(); + mScrollAnimator = null; + } + mScrollAnimator = new ValueAnimator(); + mScrollAnimator.setFloatValues(currentPositionOffsetSum, selectedIndex); // position = selectedIndex, positionOffset = 0.0f + mScrollAnimator.addUpdateListener(mAnimatorUpdateListener); + mScrollAnimator.addListener(mAnimatorListener); + mScrollAnimator.setInterpolator(mInterpolator); + mScrollAnimator.setDuration(mDuration); + mScrollAnimator.start(); + } else { + dispatchPageSelected(selectedIndex); + if (mScrollAnimator != null && mScrollAnimator.isRunning()) { + dispatchPageScrolled(mLastSelectedIndex, 0.0f, 0); + } + dispatchPageScrollStateChanged(ScrollState.SCROLL_STATE_IDLE); + dispatchPageScrolled(selectedIndex, 0.0f, 0); + } + mLastSelectedIndex = selectedIndex; + } + + public void setDuration(int duration) { + mDuration = duration; + } + + public void setInterpolator(Interpolator interpolator) { + if (interpolator == null) { + mInterpolator = new AccelerateDecelerateInterpolator(); + } else { + mInterpolator = interpolator; + } + } + + public void attachMagicIndicator(MagicIndicator magicIndicator) { + mMagicIndicators.add(magicIndicator); + } + + private void dispatchPageSelected(int pageIndex) { + for (MagicIndicator magicIndicator : mMagicIndicators) { + magicIndicator.onPageSelected(pageIndex); + } + } + + private void dispatchPageScrollStateChanged(int state) { + for (MagicIndicator magicIndicator : mMagicIndicators) { + magicIndicator.onPageScrollStateChanged(state); + } + } + + private void dispatchPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + for (MagicIndicator magicIndicator : mMagicIndicators) { + magicIndicator.onPageScrolled(position, positionOffset, positionOffsetPixels); + } + } +} diff --git a/magicindicator/src/main/java/net/lucode/hackware/magicindicator/MagicIndicator.java b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/MagicIndicator.java new file mode 100644 index 0000000..6c9a4ce --- /dev/null +++ b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/MagicIndicator.java @@ -0,0 +1,63 @@ +package net.lucode.hackware.magicindicator; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.widget.FrameLayout; + +import net.lucode.hackware.magicindicator.abs.IPagerNavigator; + +/** + * 整个框架的入口,核心 + * 博客: http://hackware.lucode.net + * Created by hackware on 2016/6/26. + */ +public class MagicIndicator extends FrameLayout { + private IPagerNavigator mNavigator; + + public MagicIndicator(Context context) { + super(context); + } + + public MagicIndicator(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + if (mNavigator != null) { + mNavigator.onPageScrolled(position, positionOffset, positionOffsetPixels); + } + } + + public void onPageSelected(int position) { + if (mNavigator != null) { + mNavigator.onPageSelected(position); + } + } + + public void onPageScrollStateChanged(int state) { + if (mNavigator != null) { + mNavigator.onPageScrollStateChanged(state); + } + } + + public IPagerNavigator getNavigator() { + return mNavigator; + } + + public void setNavigator(IPagerNavigator navigator) { + if (mNavigator == navigator) { + return; + } + if (mNavigator != null) { + mNavigator.onDetachFromMagicIndicator(); + } + mNavigator = navigator; + removeAllViews(); + if (mNavigator instanceof View) { + LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); + addView((View) mNavigator, lp); + mNavigator.onAttachToMagicIndicator(); + } + } +} diff --git a/magicindicator/src/main/java/net/lucode/hackware/magicindicator/NavigatorHelper.java b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/NavigatorHelper.java new file mode 100644 index 0000000..164efd9 --- /dev/null +++ b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/NavigatorHelper.java @@ -0,0 +1,170 @@ +package net.lucode.hackware.magicindicator; + +import android.util.SparseArray; +import android.util.SparseBooleanArray; + +/** + * 方便扩展IPagerNavigator的帮助类,将ViewPager的3个回调方法转换成 + * onSelected、onDeselected、onEnter等回调,方便扩展 + * 博客: http://hackware.lucode.net + * Created by hackware on 2016/6/26. + */ +public class NavigatorHelper { + private SparseBooleanArray mDeselectedItems = new SparseBooleanArray(); + private SparseArray mLeavedPercents = new SparseArray(); + + private int mTotalCount; + private int mCurrentIndex; + private int mLastIndex; + private float mLastPositionOffsetSum; + private int mScrollState; + + private boolean mSkimOver; + private NavigatorHelper.OnNavigatorScrollListener mNavigatorScrollListener; + + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + float currentPositionOffsetSum = position + positionOffset; + boolean leftToRight = false; + if (mLastPositionOffsetSum <= currentPositionOffsetSum) { + leftToRight = true; + } + if (mScrollState != ScrollState.SCROLL_STATE_IDLE) { + if (currentPositionOffsetSum == mLastPositionOffsetSum) { + return; + } + int nextPosition = position + 1; + boolean normalDispatch = true; + if (positionOffset == 0.0f) { + if (leftToRight) { + nextPosition = position - 1; + normalDispatch = false; + } + } + for (int i = 0; i < mTotalCount; i++) { + if (i == position || i == nextPosition) { + continue; + } + Float leavedPercent = mLeavedPercents.get(i, 0.0f); + if (leavedPercent != 1.0f) { + dispatchOnLeave(i, 1.0f, leftToRight, true); + } + } + if (normalDispatch) { + if (leftToRight) { + dispatchOnLeave(position, positionOffset, true, false); + dispatchOnEnter(nextPosition, positionOffset, true, false); + } else { + dispatchOnLeave(nextPosition, 1.0f - positionOffset, false, false); + dispatchOnEnter(position, 1.0f - positionOffset, false, false); + } + } else { + dispatchOnLeave(nextPosition, 1.0f - positionOffset, true, false); + dispatchOnEnter(position, 1.0f - positionOffset, true, false); + } + } else { + for (int i = 0; i < mTotalCount; i++) { + if (i == mCurrentIndex) { + continue; + } + boolean deselected = mDeselectedItems.get(i); + if (!deselected) { + dispatchOnDeselected(i); + } + Float leavedPercent = mLeavedPercents.get(i, 0.0f); + if (leavedPercent != 1.0f) { + dispatchOnLeave(i, 1.0f, false, true); + } + } + dispatchOnEnter(mCurrentIndex, 1.0f, false, true); + dispatchOnSelected(mCurrentIndex); + } + mLastPositionOffsetSum = currentPositionOffsetSum; + } + + private void dispatchOnEnter(int index, float enterPercent, boolean leftToRight, boolean force) { + if (mSkimOver || index == mCurrentIndex || mScrollState == ScrollState.SCROLL_STATE_DRAGGING || force) { + if (mNavigatorScrollListener != null) { + mNavigatorScrollListener.onEnter(index, mTotalCount, enterPercent, leftToRight); + } + mLeavedPercents.put(index, 1.0f - enterPercent); + } + } + + private void dispatchOnLeave(int index, float leavePercent, boolean leftToRight, boolean force) { + if (mSkimOver || index == mLastIndex || mScrollState == ScrollState.SCROLL_STATE_DRAGGING || ((index == mCurrentIndex - 1 || index == mCurrentIndex + 1) && mLeavedPercents.get(index, 0.0f) != 1.0f) || force) { + if (mNavigatorScrollListener != null) { + mNavigatorScrollListener.onLeave(index, mTotalCount, leavePercent, leftToRight); + } + mLeavedPercents.put(index, leavePercent); + } + } + + private void dispatchOnSelected(int index) { + if (mNavigatorScrollListener != null) { + mNavigatorScrollListener.onSelected(index, mTotalCount); + } + mDeselectedItems.put(index, false); + } + + private void dispatchOnDeselected(int index) { + if (mNavigatorScrollListener != null) { + mNavigatorScrollListener.onDeselected(index, mTotalCount); + } + mDeselectedItems.put(index, true); + } + + public void onPageSelected(int position) { + mLastIndex = mCurrentIndex; + mCurrentIndex = position; + dispatchOnSelected(mCurrentIndex); + for (int i = 0; i < mTotalCount; i++) { + if (i == mCurrentIndex) { + continue; + } + boolean deselected = mDeselectedItems.get(i); + if (!deselected) { + dispatchOnDeselected(i); + } + } + } + + public void onPageScrollStateChanged(int state) { + mScrollState = state; + } + + public void setNavigatorScrollListener(NavigatorHelper.OnNavigatorScrollListener navigatorScrollListener) { + mNavigatorScrollListener = navigatorScrollListener; + } + + public void setSkimOver(boolean skimOver) { + mSkimOver = skimOver; + } + + public int getTotalCount() { + return mTotalCount; + } + + public void setTotalCount(int totalCount) { + mTotalCount = totalCount; + mDeselectedItems.clear(); + mLeavedPercents.clear(); + } + + public int getCurrentIndex() { + return mCurrentIndex; + } + + public int getScrollState() { + return mScrollState; + } + + public interface OnNavigatorScrollListener { + void onEnter(int index, int totalCount, float enterPercent, boolean leftToRight); + + void onLeave(int index, int totalCount, float leavePercent, boolean leftToRight); + + void onSelected(int index, int totalCount); + + void onDeselected(int index, int totalCount); + } +} diff --git a/magicindicator/src/main/java/net/lucode/hackware/magicindicator/ScrollState.java b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/ScrollState.java new file mode 100644 index 0000000..a40dd48 --- /dev/null +++ b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/ScrollState.java @@ -0,0 +1,12 @@ +package net.lucode.hackware.magicindicator; + +/** + * 自定义滚动状态,消除对ViewPager的依赖 + * Created by hackware on 2016/8/27. + */ + +public interface ScrollState { + int SCROLL_STATE_IDLE = 0; + int SCROLL_STATE_DRAGGING = 1; + int SCROLL_STATE_SETTLING = 2; +} diff --git a/magicindicator/src/main/java/net/lucode/hackware/magicindicator/ViewPagerHelper.java b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/ViewPagerHelper.java new file mode 100644 index 0000000..14b3da3 --- /dev/null +++ b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/ViewPagerHelper.java @@ -0,0 +1,30 @@ +package net.lucode.hackware.magicindicator; + +import androidx.viewpager.widget.ViewPager; + +/** + * 简化和ViewPager绑定 + * Created by hackware on 2016/8/17. + */ + +public class ViewPagerHelper { + public static void bind(final MagicIndicator magicIndicator, ViewPager viewPager) { + viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { + + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + magicIndicator.onPageScrolled(position, positionOffset, positionOffsetPixels); + } + + @Override + public void onPageSelected(int position) { + magicIndicator.onPageSelected(position); + } + + @Override + public void onPageScrollStateChanged(int state) { + magicIndicator.onPageScrollStateChanged(state); + } + }); + } +} diff --git a/magicindicator/src/main/java/net/lucode/hackware/magicindicator/abs/IPagerNavigator.java b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/abs/IPagerNavigator.java new file mode 100644 index 0000000..4f12f0a --- /dev/null +++ b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/abs/IPagerNavigator.java @@ -0,0 +1,32 @@ +package net.lucode.hackware.magicindicator.abs; + +/** + * 抽象的ViewPager导航器 + * 博客: http://hackware.lucode.net + * Created by hackware on 2016/6/26. + */ +public interface IPagerNavigator { + + ///////////////////////// ViewPager的3个回调 + void onPageScrolled(int position, float positionOffset, int positionOffsetPixels); + + void onPageSelected(int position); + + void onPageScrollStateChanged(int state); + ///////////////////////// + + /** + * 当IPagerNavigator被添加到MagicIndicator时调用 + */ + void onAttachToMagicIndicator(); + + /** + * 当IPagerNavigator从MagicIndicator上移除时调用 + */ + void onDetachFromMagicIndicator(); + + /** + * ViewPager内容改变时需要先调用此方法,自定义的IPagerNavigator应当遵守此约定 + */ + void notifyDataSetChanged(); +} diff --git a/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/ArgbEvaluatorHolder.java b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/ArgbEvaluatorHolder.java new file mode 100644 index 0000000..c5c7b1c --- /dev/null +++ b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/ArgbEvaluatorHolder.java @@ -0,0 +1,28 @@ +package net.lucode.hackware.magicindicator.buildins; + + +/** + * 实现颜色渐变,考虑到兼容性,不使用内置的ArgbEvaluator + * 博客: http://hackware.lucode.net + * Created by hackware on 2016/6/26. + */ +public class ArgbEvaluatorHolder { + public static int eval(float fraction, int startValue, int endValue) { + int startA = (startValue >> 24) & 0xff; + int startR = (startValue >> 16) & 0xff; + int startG = (startValue >> 8) & 0xff; + int startB = startValue & 0xff; + + int endA = (endValue >> 24) & 0xff; + int endR = (endValue >> 16) & 0xff; + int endG = (endValue >> 8) & 0xff; + int endB = endValue & 0xff; + + int currentA = (startA + (int) (fraction * (endA - startA))) << 24; + int currentR = (startR + (int) (fraction * (endR - startR))) << 16; + int currentG = (startG + (int) (fraction * (endG - startG))) << 8; + int currentB = startB + (int) (fraction * (endB - startB)); + + return currentA | currentR | currentG | currentB; + } +} diff --git a/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/UIUtil.java b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/UIUtil.java new file mode 100644 index 0000000..9ad3142 --- /dev/null +++ b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/UIUtil.java @@ -0,0 +1,19 @@ +package net.lucode.hackware.magicindicator.buildins; + +import android.content.Context; + +/** + * 博客: http://hackware.lucode.net + * Created by hackware on 2016/6/26. + */ +public final class UIUtil { + + public static int dip2px(Context context, double dpValue) { + float density = context.getResources().getDisplayMetrics().density; + return (int) (dpValue * density + 0.5); + } + + public static int getScreenWidth(Context context) { + return context.getResources().getDisplayMetrics().widthPixels; + } +} \ No newline at end of file diff --git a/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/circlenavigator/CircleNavigator.java b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/circlenavigator/CircleNavigator.java new file mode 100644 index 0000000..bd334b3 --- /dev/null +++ b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/circlenavigator/CircleNavigator.java @@ -0,0 +1,309 @@ +package net.lucode.hackware.magicindicator.buildins.circlenavigator; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.PointF; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.animation.Interpolator; +import android.view.animation.LinearInterpolator; + +import net.lucode.hackware.magicindicator.abs.IPagerNavigator; +import net.lucode.hackware.magicindicator.buildins.UIUtil; + +import java.util.ArrayList; +import java.util.List; + +/** + * 圆圈式的指示器 + * 博客: http://hackware.lucode.net + * Created by hackware on 2016/6/26. + */ +public class CircleNavigator extends View implements IPagerNavigator { + private int mRadius; + private int mCircleColor; + private int mStrokeWidth; + private int mCircleSpacing; + private int mCurrentIndex; + private int mTotalCount; + private Interpolator mStartInterpolator = new LinearInterpolator(); + + private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private List mCirclePoints = new ArrayList(); + private float mIndicatorX; + + // 事件回调 + private boolean mTouchable; + private OnCircleClickListener mCircleClickListener; + private float mDownX; + private float mDownY; + private int mTouchSlop; + + private boolean mFollowTouch = true; // 是否跟随手指滑动 + + public CircleNavigator(Context context) { + super(context); + init(context); + } + + private void init(Context context) { + mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); + mRadius = UIUtil.dip2px(context, 3); + mCircleSpacing = UIUtil.dip2px(context, 8); + mStrokeWidth = UIUtil.dip2px(context, 1); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec)); + } + + private int measureWidth(int widthMeasureSpec) { + int mode = MeasureSpec.getMode(widthMeasureSpec); + int width = MeasureSpec.getSize(widthMeasureSpec); + int result = 0; + switch (mode) { + case MeasureSpec.EXACTLY: + result = width; + break; + case MeasureSpec.AT_MOST: + case MeasureSpec.UNSPECIFIED: + result = mTotalCount * mRadius * 2 + (mTotalCount - 1) * mCircleSpacing + getPaddingLeft() + getPaddingRight() + mStrokeWidth * 2; + break; + default: + break; + } + return result; + } + + private int measureHeight(int heightMeasureSpec) { + int mode = MeasureSpec.getMode(heightMeasureSpec); + int height = MeasureSpec.getSize(heightMeasureSpec); + int result = 0; + switch (mode) { + case MeasureSpec.EXACTLY: + result = height; + break; + case MeasureSpec.AT_MOST: + case MeasureSpec.UNSPECIFIED: + result = mRadius * 2 + mStrokeWidth * 2 + getPaddingTop() + getPaddingBottom(); + break; + default: + break; + } + return result; + } + + @Override + protected void onDraw(Canvas canvas) { + mPaint.setColor(mCircleColor); + drawCircles(canvas); + drawIndicator(canvas); + } + + private void drawCircles(Canvas canvas) { + mPaint.setStyle(Paint.Style.STROKE); + mPaint.setStrokeWidth(mStrokeWidth); + for (int i = 0, j = mCirclePoints.size(); i < j; i++) { + PointF pointF = mCirclePoints.get(i); + canvas.drawCircle(pointF.x, pointF.y, mRadius, mPaint); + } + } + + private void drawIndicator(Canvas canvas) { + mPaint.setStyle(Paint.Style.FILL); + if (mCirclePoints.size() > 0) { + canvas.drawCircle(mIndicatorX, (int) (getHeight() / 2.0f + 0.5f), mRadius, mPaint); + } + } + + private void prepareCirclePoints() { + mCirclePoints.clear(); + if (mTotalCount > 0) { + int y = (int) (getHeight() / 2.0f + 0.5f); + int centerSpacing = mRadius * 2 + mCircleSpacing; + int startX = mRadius + (int) (mStrokeWidth / 2.0f + 0.5f) + getPaddingLeft(); + for (int i = 0; i < mTotalCount; i++) { + PointF pointF = new PointF(startX, y); + mCirclePoints.add(pointF); + startX += centerSpacing; + } + mIndicatorX = mCirclePoints.get(mCurrentIndex).x; + } + } + + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + if (mFollowTouch) { + if (mCirclePoints.isEmpty()) { + return; + } + + int currentPosition = Math.min(mCirclePoints.size() - 1, position); + int nextPosition = Math.min(mCirclePoints.size() - 1, position + 1); + PointF current = mCirclePoints.get(currentPosition); + PointF next = mCirclePoints.get(nextPosition); + + mIndicatorX = current.x + (next.x - current.x) * mStartInterpolator.getInterpolation(positionOffset); + + invalidate(); + } + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + float x = event.getX(); + float y = event.getY(); + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + if (mTouchable) { + mDownX = x; + mDownY = y; + return true; + } + break; + case MotionEvent.ACTION_UP: + if (mCircleClickListener != null) { + if (Math.abs(x - mDownX) <= mTouchSlop && Math.abs(y - mDownY) <= mTouchSlop) { + float max = Float.MAX_VALUE; + int index = 0; + for (int i = 0; i < mCirclePoints.size(); i++) { + PointF pointF = mCirclePoints.get(i); + float offset = Math.abs(pointF.x - x); + if (offset < max) { + max = offset; + index = i; + } + } + mCircleClickListener.onClick(index); + } + } + break; + default: + break; + } + return super.onTouchEvent(event); + } + + @Override + public void onPageSelected(int position) { + mCurrentIndex = position; + if (!mFollowTouch) { + mIndicatorX = mCirclePoints.get(mCurrentIndex).x; + invalidate(); + } + } + + @Override + public void onPageScrollStateChanged(int state) { + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + prepareCirclePoints(); + } + + @Override + public void onAttachToMagicIndicator() { + } + + @Override + public void notifyDataSetChanged() { + prepareCirclePoints(); + invalidate(); + } + + @Override + public void onDetachFromMagicIndicator() { + } + + public int getRadius() { + return mRadius; + } + + public void setRadius(int radius) { + mRadius = radius; + prepareCirclePoints(); + invalidate(); + } + + public int getCircleColor() { + return mCircleColor; + } + + public void setCircleColor(int circleColor) { + mCircleColor = circleColor; + invalidate(); + } + + public int getStrokeWidth() { + return mStrokeWidth; + } + + public void setStrokeWidth(int strokeWidth) { + mStrokeWidth = strokeWidth; + invalidate(); + } + + public int getCircleSpacing() { + return mCircleSpacing; + } + + public void setCircleSpacing(int circleSpacing) { + mCircleSpacing = circleSpacing; + prepareCirclePoints(); + invalidate(); + } + + public Interpolator getStartInterpolator() { + return mStartInterpolator; + } + + public void setStartInterpolator(Interpolator startInterpolator) { + mStartInterpolator = startInterpolator; + if (mStartInterpolator == null) { + mStartInterpolator = new LinearInterpolator(); + } + } + + public int getCircleCount() { + return mTotalCount; + } + + public void setCircleCount(int count) { + mTotalCount = count; // 此处不调用invalidate,让外部调用notifyDataSetChanged + } + + public boolean isTouchable() { + return mTouchable; + } + + public void setTouchable(boolean touchable) { + mTouchable = touchable; + } + + public boolean isFollowTouch() { + return mFollowTouch; + } + + public void setFollowTouch(boolean followTouch) { + mFollowTouch = followTouch; + } + + public OnCircleClickListener getCircleClickListener() { + return mCircleClickListener; + } + + public void setCircleClickListener(OnCircleClickListener circleClickListener) { + if (!mTouchable) { + mTouchable = true; + } + mCircleClickListener = circleClickListener; + } + + public interface OnCircleClickListener { + void onClick(int index); + } +} diff --git a/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/CommonNavigator.java b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/CommonNavigator.java new file mode 100644 index 0000000..a40c787 --- /dev/null +++ b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/CommonNavigator.java @@ -0,0 +1,425 @@ +package net.lucode.hackware.magicindicator.buildins.commonnavigator; + +import android.content.Context; +import android.database.DataSetObserver; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.HorizontalScrollView; +import android.widget.LinearLayout; + +import net.lucode.hackware.magicindicator.NavigatorHelper; +import net.lucode.hackware.magicindicator.R; +import net.lucode.hackware.magicindicator.ScrollState; +import net.lucode.hackware.magicindicator.abs.IPagerNavigator; +import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.CommonNavigatorAdapter; +import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IMeasurablePagerTitleView; +import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IPagerIndicator; +import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IPagerTitleView; +import net.lucode.hackware.magicindicator.buildins.commonnavigator.model.PositionData; + +import java.util.ArrayList; +import java.util.List; + +/** + * 通用的ViewPager指示器,包含PagerTitle和PagerIndicator + * 博客: http://hackware.lucode.net + * Created by hackware on 2016/6/26. + */ +public class CommonNavigator extends FrameLayout implements IPagerNavigator, NavigatorHelper.OnNavigatorScrollListener { + private HorizontalScrollView mScrollView; + private LinearLayout mTitleContainer; + private LinearLayout mIndicatorContainer; + private IPagerIndicator mIndicator; + + private CommonNavigatorAdapter mAdapter; + private NavigatorHelper mNavigatorHelper; + + /** + * 提供给外部的参数配置 + */ + /****************************************************/ + private boolean mAdjustMode; // 自适应模式,适用于数目固定的、少量的title + private boolean mEnablePivotScroll; // 启动中心点滚动 + private float mScrollPivotX = 0.5f; // 滚动中心点 0.0f - 1.0f + private boolean mSmoothScroll = true; // 是否平滑滚动,适用于 !mAdjustMode && !mFollowTouch + private boolean mFollowTouch = true; // 是否手指跟随滚动 + private int mRightPadding; + private int mLeftPadding; + private boolean mIndicatorOnTop; // 指示器是否在title上层,默认为下层 + private boolean mSkimOver; // 跨多页切换时,中间页是否显示 "掠过" 效果 + private boolean mReselectWhenLayout = true; // PositionData准备好时,是否重新选中当前页,为true可保证在极端情况下指示器状态正确 + /****************************************************/ + + // 保存每个title的位置信息,为扩展indicator提供保障 + private List mPositionDataList = new ArrayList(); + + private DataSetObserver mObserver = new DataSetObserver() { + + @Override + public void onChanged() { + mNavigatorHelper.setTotalCount(mAdapter.getCount()); // 如果使用helper,应始终保证helper中的totalCount为最新 + init(); + } + + @Override + public void onInvalidated() { + // 没什么用,暂不做处理 + } + }; + + public CommonNavigator(Context context) { + super(context); + mNavigatorHelper = new NavigatorHelper(); + mNavigatorHelper.setNavigatorScrollListener(this); + } + + @Override + public void notifyDataSetChanged() { + if (mAdapter != null) { + mAdapter.notifyDataSetChanged(); + } + } + + public boolean isAdjustMode() { + return mAdjustMode; + } + + public void setAdjustMode(boolean is) { + mAdjustMode = is; + } + + public CommonNavigatorAdapter getAdapter() { + return mAdapter; + } + + public void setAdapter(CommonNavigatorAdapter adapter) { + if (mAdapter == adapter) { + return; + } + if (mAdapter != null) { + mAdapter.unregisterDataSetObserver(mObserver); + } + mAdapter = adapter; + if (mAdapter != null) { + mAdapter.registerDataSetObserver(mObserver); + mNavigatorHelper.setTotalCount(mAdapter.getCount()); + if (mTitleContainer != null) { // adapter改变时,应该重新init,但是第一次设置adapter不用,onAttachToMagicIndicator中有init + mAdapter.notifyDataSetChanged(); + } + } else { + mNavigatorHelper.setTotalCount(0); + init(); + } + } + + private void init() { + removeAllViews(); + + View root; + if (mAdjustMode) { + root = LayoutInflater.from(getContext()).inflate(R.layout.pager_navigator_layout_no_scroll, this); + } else { + root = LayoutInflater.from(getContext()).inflate(R.layout.pager_navigator_layout, this); + } + + mScrollView = (HorizontalScrollView) root.findViewById(R.id.scroll_view); // mAdjustMode为true时,mScrollView为null + + mTitleContainer = (LinearLayout) root.findViewById(R.id.title_container); + mTitleContainer.setPadding(mLeftPadding, 0, mRightPadding, 0); + + mIndicatorContainer = (LinearLayout) root.findViewById(R.id.indicator_container); + if (mIndicatorOnTop) { + mIndicatorContainer.getParent().bringChildToFront(mIndicatorContainer); + } + + initTitlesAndIndicator(); + } + + /** + * 初始化title和indicator + */ + private void initTitlesAndIndicator() { + for (int i = 0, j = mNavigatorHelper.getTotalCount(); i < j; i++) { + IPagerTitleView v = mAdapter.getTitleView(getContext(), i); + if (v instanceof View) { + View view = (View) v; + LinearLayout.LayoutParams lp; + if (mAdjustMode) { + lp = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT); + lp.weight = mAdapter.getTitleWeight(getContext(), i); + } else { + lp = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); + } + mTitleContainer.addView(view, lp); + } + } + if (mAdapter != null) { + mIndicator = mAdapter.getIndicator(getContext()); + if (mIndicator instanceof View) { + LayoutParams lp = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); + mIndicatorContainer.addView((View) mIndicator, lp); + } + } + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + if (mAdapter != null) { + preparePositionData(); + if (mIndicator != null) { + mIndicator.onPositionDataProvide(mPositionDataList); + } + if (mReselectWhenLayout && mNavigatorHelper.getScrollState() == ScrollState.SCROLL_STATE_IDLE) { + onPageSelected(mNavigatorHelper.getCurrentIndex()); + onPageScrolled(mNavigatorHelper.getCurrentIndex(), 0.0f, 0); + } + } + } + + /** + * 获取title的位置信息,为打造不同的指示器、各种效果提供可能 + */ + private void preparePositionData() { + mPositionDataList.clear(); + for (int i = 0, j = mNavigatorHelper.getTotalCount(); i < j; i++) { + PositionData data = new PositionData(); + View v = mTitleContainer.getChildAt(i); + if (v != null) { + data.mLeft = v.getLeft(); + data.mTop = v.getTop(); + data.mRight = v.getRight(); + data.mBottom = v.getBottom(); + if (v instanceof IMeasurablePagerTitleView) { + IMeasurablePagerTitleView view = (IMeasurablePagerTitleView) v; + data.mContentLeft = view.getContentLeft(); + data.mContentTop = view.getContentTop(); + data.mContentRight = view.getContentRight(); + data.mContentBottom = view.getContentBottom(); + } else { + data.mContentLeft = data.mLeft; + data.mContentTop = data.mTop; + data.mContentRight = data.mRight; + data.mContentBottom = data.mBottom; + } + } + mPositionDataList.add(data); + } + } + + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + if (mAdapter != null) { + + mNavigatorHelper.onPageScrolled(position, positionOffset, positionOffsetPixels); + if (mIndicator != null) { + mIndicator.onPageScrolled(position, positionOffset, positionOffsetPixels); + } + + // 手指跟随滚动 + if (mScrollView != null && mPositionDataList.size() > 0 && position >= 0 && position < mPositionDataList.size()) { + if (mFollowTouch) { + int currentPosition = Math.min(mPositionDataList.size() - 1, position); + int nextPosition = Math.min(mPositionDataList.size() - 1, position + 1); + PositionData current = mPositionDataList.get(currentPosition); + PositionData next = mPositionDataList.get(nextPosition); + float scrollTo = current.horizontalCenter() - mScrollView.getWidth() * mScrollPivotX; + float nextScrollTo = next.horizontalCenter() - mScrollView.getWidth() * mScrollPivotX; + mScrollView.scrollTo((int) (scrollTo + (nextScrollTo - scrollTo) * positionOffset), 0); + } else if (!mEnablePivotScroll) { + // TODO 实现待选中项完全显示出来 + } + } + } + } + + public float getScrollPivotX() { + return mScrollPivotX; + } + + public void setScrollPivotX(float scrollPivotX) { + mScrollPivotX = scrollPivotX; + } + + @Override + public void onPageSelected(int position) { + if (mAdapter != null) { + mNavigatorHelper.onPageSelected(position); + if (mIndicator != null) { + mIndicator.onPageSelected(position); + } + } + } + + @Override + public void onPageScrollStateChanged(int state) { + if (mAdapter != null) { + mNavigatorHelper.onPageScrollStateChanged(state); + if (mIndicator != null) { + mIndicator.onPageScrollStateChanged(state); + } + } + } + + @Override + public void onAttachToMagicIndicator() { + init(); // 将初始化延迟到这里 + } + + @Override + public void onDetachFromMagicIndicator() { + } + + public IPagerIndicator getPagerIndicator() { + return mIndicator; + } + + public boolean isEnablePivotScroll() { + return mEnablePivotScroll; + } + + public void setEnablePivotScroll(boolean is) { + mEnablePivotScroll = is; + } + + @Override + public void onEnter(int index, int totalCount, float enterPercent, boolean leftToRight) { + if (mTitleContainer == null) { + return; + } + View v = mTitleContainer.getChildAt(index); + if (v instanceof IPagerTitleView) { + ((IPagerTitleView) v).onEnter(index, totalCount, enterPercent, leftToRight); + } + } + + @Override + public void onLeave(int index, int totalCount, float leavePercent, boolean leftToRight) { + if (mTitleContainer == null) { + return; + } + View v = mTitleContainer.getChildAt(index); + if (v instanceof IPagerTitleView) { + ((IPagerTitleView) v).onLeave(index, totalCount, leavePercent, leftToRight); + } + } + + public boolean isSmoothScroll() { + return mSmoothScroll; + } + + public void setSmoothScroll(boolean smoothScroll) { + mSmoothScroll = smoothScroll; + } + + public boolean isFollowTouch() { + return mFollowTouch; + } + + public void setFollowTouch(boolean followTouch) { + mFollowTouch = followTouch; + } + + public boolean isSkimOver() { + return mSkimOver; + } + + public void setSkimOver(boolean skimOver) { + mSkimOver = skimOver; + mNavigatorHelper.setSkimOver(skimOver); + } + + @Override + public void onSelected(int index, int totalCount) { + if (mTitleContainer == null) { + return; + } + View v = mTitleContainer.getChildAt(index); + if (v instanceof IPagerTitleView) { + ((IPagerTitleView) v).onSelected(index, totalCount); + } + if (!mAdjustMode && !mFollowTouch && mScrollView != null && mPositionDataList.size() > 0) { + int currentIndex = Math.min(mPositionDataList.size() - 1, index); + PositionData current = mPositionDataList.get(currentIndex); + if (mEnablePivotScroll) { + float scrollTo = current.horizontalCenter() - mScrollView.getWidth() * mScrollPivotX; + if (mSmoothScroll) { + mScrollView.smoothScrollTo((int) (scrollTo), 0); + } else { + mScrollView.scrollTo((int) (scrollTo), 0); + } + } else { + // 如果当前项被部分遮挡,则滚动显示完全 + if (mScrollView.getScrollX() > current.mLeft) { + if (mSmoothScroll) { + mScrollView.smoothScrollTo(current.mLeft, 0); + } else { + mScrollView.scrollTo(current.mLeft, 0); + } + } else if (mScrollView.getScrollX() + getWidth() < current.mRight) { + if (mSmoothScroll) { + mScrollView.smoothScrollTo(current.mRight - getWidth(), 0); + } else { + mScrollView.scrollTo(current.mRight - getWidth(), 0); + } + } + } + } + } + + @Override + public void onDeselected(int index, int totalCount) { + if (mTitleContainer == null) { + return; + } + View v = mTitleContainer.getChildAt(index); + if (v instanceof IPagerTitleView) { + ((IPagerTitleView) v).onDeselected(index, totalCount); + } + } + + public IPagerTitleView getPagerTitleView(int index) { + if (mTitleContainer == null) { + return null; + } + return (IPagerTitleView) mTitleContainer.getChildAt(index); + } + + public LinearLayout getTitleContainer() { + return mTitleContainer; + } + + public int getRightPadding() { + return mRightPadding; + } + + public void setRightPadding(int rightPadding) { + mRightPadding = rightPadding; + } + + public int getLeftPadding() { + return mLeftPadding; + } + + public void setLeftPadding(int leftPadding) { + mLeftPadding = leftPadding; + } + + public boolean isIndicatorOnTop() { + return mIndicatorOnTop; + } + + public void setIndicatorOnTop(boolean indicatorOnTop) { + mIndicatorOnTop = indicatorOnTop; + } + + public boolean isReselectWhenLayout() { + return mReselectWhenLayout; + } + + public void setReselectWhenLayout(boolean reselectWhenLayout) { + mReselectWhenLayout = reselectWhenLayout; + } +} diff --git a/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/abs/CommonNavigatorAdapter.java b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/abs/CommonNavigatorAdapter.java new file mode 100644 index 0000000..f3dd401 --- /dev/null +++ b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/abs/CommonNavigatorAdapter.java @@ -0,0 +1,41 @@ +package net.lucode.hackware.magicindicator.buildins.commonnavigator.abs; + +import android.content.Context; +import android.database.DataSetObservable; +import android.database.DataSetObserver; + +/** + * CommonNavigator适配器,通过它可轻松切换不同的指示器样式 + * 博客: http://hackware.lucode.net + * Created by hackware on 2016/6/26. + */ +public abstract class CommonNavigatorAdapter { + + private final DataSetObservable mDataSetObservable = new DataSetObservable(); + + public abstract int getCount(); + + public abstract IPagerTitleView getTitleView(Context context, int index); + + public abstract IPagerIndicator getIndicator(Context context); + + public float getTitleWeight(Context context, int index) { + return 1; + } + + public final void registerDataSetObserver(DataSetObserver observer) { + mDataSetObservable.registerObserver(observer); + } + + public final void unregisterDataSetObserver(DataSetObserver observer) { + mDataSetObservable.unregisterObserver(observer); + } + + public final void notifyDataSetChanged() { + mDataSetObservable.notifyChanged(); + } + + public final void notifyDataSetInvalidated() { + mDataSetObservable.notifyInvalidated(); + } +} diff --git a/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/abs/IMeasurablePagerTitleView.java b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/abs/IMeasurablePagerTitleView.java new file mode 100644 index 0000000..181d3e5 --- /dev/null +++ b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/abs/IMeasurablePagerTitleView.java @@ -0,0 +1,16 @@ +package net.lucode.hackware.magicindicator.buildins.commonnavigator.abs; + +/** + * 可测量内容区域的指示器标题 + * 博客: http://hackware.lucode.net + * Created by hackware on 2016/6/26. + */ +public interface IMeasurablePagerTitleView extends IPagerTitleView { + int getContentLeft(); + + int getContentTop(); + + int getContentRight(); + + int getContentBottom(); +} diff --git a/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/abs/IPagerIndicator.java b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/abs/IPagerIndicator.java new file mode 100644 index 0000000..c7b08d1 --- /dev/null +++ b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/abs/IPagerIndicator.java @@ -0,0 +1,21 @@ +package net.lucode.hackware.magicindicator.buildins.commonnavigator.abs; + + +import net.lucode.hackware.magicindicator.buildins.commonnavigator.model.PositionData; + +import java.util.List; + +/** + * 抽象的viewpager指示器,适用于CommonNavigator + * 博客: http://hackware.lucode.net + * Created by hackware on 2016/6/26. + */ +public interface IPagerIndicator { + void onPageScrolled(int position, float positionOffset, int positionOffsetPixels); + + void onPageSelected(int position); + + void onPageScrollStateChanged(int state); + + void onPositionDataProvide(List dataList); +} diff --git a/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/abs/IPagerTitleView.java b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/abs/IPagerTitleView.java new file mode 100644 index 0000000..069b3fc --- /dev/null +++ b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/abs/IPagerTitleView.java @@ -0,0 +1,34 @@ +package net.lucode.hackware.magicindicator.buildins.commonnavigator.abs; + +/** + * 抽象的指示器标题,适用于CommonNavigator + * 博客: http://hackware.lucode.net + * Created by hackware on 2016/6/26. + */ +public interface IPagerTitleView { + /** + * 被选中 + */ + void onSelected(int index, int totalCount); + + /** + * 未被选中 + */ + void onDeselected(int index, int totalCount); + + /** + * 离开 + * + * @param leavePercent 离开的百分比, 0.0f - 1.0f + * @param leftToRight 从左至右离开 + */ + void onLeave(int index, int totalCount, float leavePercent, boolean leftToRight); + + /** + * 进入 + * + * @param enterPercent 进入的百分比, 0.0f - 1.0f + * @param leftToRight 从左至右离开 + */ + void onEnter(int index, int totalCount, float enterPercent, boolean leftToRight); +} diff --git a/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/indicators/BezierPagerIndicator.java b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/indicators/BezierPagerIndicator.java new file mode 100644 index 0000000..1e679e7 --- /dev/null +++ b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/indicators/BezierPagerIndicator.java @@ -0,0 +1,165 @@ +package net.lucode.hackware.magicindicator.buildins.commonnavigator.indicators; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Path; +import android.view.View; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.DecelerateInterpolator; +import android.view.animation.Interpolator; + +import net.lucode.hackware.magicindicator.FragmentContainerHelper; +import net.lucode.hackware.magicindicator.buildins.ArgbEvaluatorHolder; +import net.lucode.hackware.magicindicator.buildins.UIUtil; +import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IPagerIndicator; +import net.lucode.hackware.magicindicator.buildins.commonnavigator.model.PositionData; + +import java.util.Arrays; +import java.util.List; + +/** + * 贝塞尔曲线ViewPager指示器,带颜色渐变 + * 博客: http://hackware.lucode.net + * Created by hackware on 2016/6/26. + */ +public class BezierPagerIndicator extends View implements IPagerIndicator { + private List mPositionDataList; + + private float mLeftCircleRadius; + private float mLeftCircleX; + private float mRightCircleRadius; + private float mRightCircleX; + + private float mYOffset; + private float mMaxCircleRadius; + private float mMinCircleRadius; + + private Paint mPaint; + private Path mPath = new Path(); + + private List mColors; + private Interpolator mStartInterpolator = new AccelerateInterpolator(); + private Interpolator mEndInterpolator = new DecelerateInterpolator(); + + public BezierPagerIndicator(Context context) { + super(context); + init(context); + } + + private void init(Context context) { + mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mPaint.setStyle(Paint.Style.FILL); + mMaxCircleRadius = UIUtil.dip2px(context, 3.5); + mMinCircleRadius = UIUtil.dip2px(context, 2); + mYOffset = UIUtil.dip2px(context, 1.5); + } + + @Override + protected void onDraw(Canvas canvas) { + canvas.drawCircle(mLeftCircleX, getHeight() - mYOffset - mMaxCircleRadius, mLeftCircleRadius, mPaint); + canvas.drawCircle(mRightCircleX, getHeight() - mYOffset - mMaxCircleRadius, mRightCircleRadius, mPaint); + drawBezierCurve(canvas); + } + + /** + * 绘制贝塞尔曲线 + * + * @param canvas + */ + private void drawBezierCurve(Canvas canvas) { + mPath.reset(); + float y = getHeight() - mYOffset - mMaxCircleRadius; + mPath.moveTo(mRightCircleX, y); + mPath.lineTo(mRightCircleX, y - mRightCircleRadius); + mPath.quadTo(mRightCircleX + (mLeftCircleX - mRightCircleX) / 2.0f, y, mLeftCircleX, y - mLeftCircleRadius); + mPath.lineTo(mLeftCircleX, y + mLeftCircleRadius); + mPath.quadTo(mRightCircleX + (mLeftCircleX - mRightCircleX) / 2.0f, y, mRightCircleX, y + mRightCircleRadius); + mPath.close(); // 闭合 + canvas.drawPath(mPath, mPaint); + } + + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + if (mPositionDataList == null || mPositionDataList.isEmpty()) { + return; + } + + // 计算颜色 + if (mColors != null && mColors.size() > 0) { + int currentColor = mColors.get(Math.abs(position) % mColors.size()); + int nextColor = mColors.get(Math.abs(position + 1) % mColors.size()); + int color = ArgbEvaluatorHolder.eval(positionOffset, currentColor, nextColor); + mPaint.setColor(color); + } + + // 计算锚点位置 + PositionData current = FragmentContainerHelper.getImitativePositionData(mPositionDataList, position); + PositionData next = FragmentContainerHelper.getImitativePositionData(mPositionDataList, position + 1); + + float leftX = current.mLeft + (current.mRight - current.mLeft) / 2; + float rightX = next.mLeft + (next.mRight - next.mLeft) / 2; + + mLeftCircleX = leftX + (rightX - leftX) * mStartInterpolator.getInterpolation(positionOffset); + mRightCircleX = leftX + (rightX - leftX) * mEndInterpolator.getInterpolation(positionOffset); + mLeftCircleRadius = mMaxCircleRadius + (mMinCircleRadius - mMaxCircleRadius) * mEndInterpolator.getInterpolation(positionOffset); + mRightCircleRadius = mMinCircleRadius + (mMaxCircleRadius - mMinCircleRadius) * mStartInterpolator.getInterpolation(positionOffset); + + invalidate(); + } + + @Override + public void onPageSelected(int position) { + } + + @Override + public void onPageScrollStateChanged(int state) { + } + + @Override + public void onPositionDataProvide(List dataList) { + mPositionDataList = dataList; + } + + public float getMaxCircleRadius() { + return mMaxCircleRadius; + } + + public void setMaxCircleRadius(float maxCircleRadius) { + mMaxCircleRadius = maxCircleRadius; + } + + public float getMinCircleRadius() { + return mMinCircleRadius; + } + + public void setMinCircleRadius(float minCircleRadius) { + mMinCircleRadius = minCircleRadius; + } + + public float getYOffset() { + return mYOffset; + } + + public void setYOffset(float yOffset) { + mYOffset = yOffset; + } + + public void setColors(Integer... colors) { + mColors = Arrays.asList(colors); + } + + public void setStartInterpolator(Interpolator startInterpolator) { + mStartInterpolator = startInterpolator; + if (mStartInterpolator == null) { + mStartInterpolator = new AccelerateInterpolator(); + } + } + + public void setEndInterpolator(Interpolator endInterpolator) { + mEndInterpolator = endInterpolator; + if (mEndInterpolator == null) { + mEndInterpolator = new DecelerateInterpolator(); + } + } +} diff --git a/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/indicators/LinePagerIndicator.java b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/indicators/LinePagerIndicator.java new file mode 100644 index 0000000..0d890f8 --- /dev/null +++ b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/indicators/LinePagerIndicator.java @@ -0,0 +1,210 @@ +package net.lucode.hackware.magicindicator.buildins.commonnavigator.indicators; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.RectF; +import android.view.View; +import android.view.animation.Interpolator; +import android.view.animation.LinearInterpolator; + +import net.lucode.hackware.magicindicator.FragmentContainerHelper; +import net.lucode.hackware.magicindicator.buildins.ArgbEvaluatorHolder; +import net.lucode.hackware.magicindicator.buildins.UIUtil; +import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IPagerIndicator; +import net.lucode.hackware.magicindicator.buildins.commonnavigator.model.PositionData; + +import java.util.Arrays; +import java.util.List; + +/** + * 直线viewpager指示器,带颜色渐变 + * 博客: http://hackware.lucode.net + * Created by hackware on 2016/6/26. + */ +public class LinePagerIndicator extends View implements IPagerIndicator { + public static final int MODE_MATCH_EDGE = 0; // 直线宽度 == title宽度 - 2 * mXOffset + public static final int MODE_WRAP_CONTENT = 1; // 直线宽度 == title内容宽度 - 2 * mXOffset + public static final int MODE_EXACTLY = 2; // 直线宽度 == mLineWidth + + private int mMode; // 默认为MODE_MATCH_EDGE模式 + + // 控制动画 + private Interpolator mStartInterpolator = new LinearInterpolator(); + private Interpolator mEndInterpolator = new LinearInterpolator(); + + private float mYOffset; // 相对于底部的偏移量,如果你想让直线位于title上方,设置它即可 + private float mLineHeight; + private float mXOffset; + private float mLineWidth; + private float mRoundRadius; + + private Paint mPaint; + private List mPositionDataList; + private List mColors; + + private RectF mLineRect = new RectF(); + + public LinePagerIndicator(Context context) { + super(context); + init(context); + } + + private void init(Context context) { + mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mPaint.setStyle(Paint.Style.FILL); + mLineHeight = UIUtil.dip2px(context, 3); + mLineWidth = UIUtil.dip2px(context, 10); + } + + @Override + protected void onDraw(Canvas canvas) { + canvas.drawRoundRect(mLineRect, mRoundRadius, mRoundRadius, mPaint); + } + + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + if (mPositionDataList == null || mPositionDataList.isEmpty()) { + return; + } + + // 计算颜色 + if (mColors != null && mColors.size() > 0) { + int currentColor = mColors.get(Math.abs(position) % mColors.size()); + int nextColor = mColors.get(Math.abs(position + 1) % mColors.size()); + int color = ArgbEvaluatorHolder.eval(positionOffset, currentColor, nextColor); + mPaint.setColor(color); + } + + // 计算锚点位置 + PositionData current = FragmentContainerHelper.getImitativePositionData(mPositionDataList, position); + PositionData next = FragmentContainerHelper.getImitativePositionData(mPositionDataList, position + 1); + + float leftX; + float nextLeftX; + float rightX; + float nextRightX; + if (mMode == MODE_MATCH_EDGE) { + leftX = current.mLeft + mXOffset; + nextLeftX = next.mLeft + mXOffset; + rightX = current.mRight - mXOffset; + nextRightX = next.mRight - mXOffset; + } else if (mMode == MODE_WRAP_CONTENT) { + leftX = current.mContentLeft + mXOffset; + nextLeftX = next.mContentLeft + mXOffset; + rightX = current.mContentRight - mXOffset; + nextRightX = next.mContentRight - mXOffset; + } else { // MODE_EXACTLY + leftX = current.mLeft + (current.width() - mLineWidth) / 2; + nextLeftX = next.mLeft + (next.width() - mLineWidth) / 2; + rightX = current.mLeft + (current.width() + mLineWidth) / 2; + nextRightX = next.mLeft + (next.width() + mLineWidth) / 2; + } + + mLineRect.left = leftX + (nextLeftX - leftX) * mStartInterpolator.getInterpolation(positionOffset); + mLineRect.right = rightX + (nextRightX - rightX) * mEndInterpolator.getInterpolation(positionOffset); + mLineRect.top = getHeight() - mLineHeight - mYOffset; + mLineRect.bottom = getHeight() - mYOffset; + + invalidate(); + } + + @Override + public void onPageSelected(int position) { + } + + @Override + public void onPageScrollStateChanged(int state) { + } + + @Override + public void onPositionDataProvide(List dataList) { + mPositionDataList = dataList; + } + + public float getYOffset() { + return mYOffset; + } + + public void setYOffset(float yOffset) { + mYOffset = yOffset; + } + + public float getXOffset() { + return mXOffset; + } + + public void setXOffset(float xOffset) { + mXOffset = xOffset; + } + + public float getLineHeight() { + return mLineHeight; + } + + public void setLineHeight(float lineHeight) { + mLineHeight = lineHeight; + } + + public float getLineWidth() { + return mLineWidth; + } + + public void setLineWidth(float lineWidth) { + mLineWidth = lineWidth; + } + + public float getRoundRadius() { + return mRoundRadius; + } + + public void setRoundRadius(float roundRadius) { + mRoundRadius = roundRadius; + } + + public int getMode() { + return mMode; + } + + public void setMode(int mode) { + if (mode == MODE_EXACTLY || mode == MODE_MATCH_EDGE || mode == MODE_WRAP_CONTENT) { + mMode = mode; + } else { + throw new IllegalArgumentException("mode " + mode + " not supported."); + } + } + + public Paint getPaint() { + return mPaint; + } + + public List getColors() { + return mColors; + } + + public void setColors(Integer... colors) { + mColors = Arrays.asList(colors); + } + + public Interpolator getStartInterpolator() { + return mStartInterpolator; + } + + public void setStartInterpolator(Interpolator startInterpolator) { + mStartInterpolator = startInterpolator; + if (mStartInterpolator == null) { + mStartInterpolator = new LinearInterpolator(); + } + } + + public Interpolator getEndInterpolator() { + return mEndInterpolator; + } + + public void setEndInterpolator(Interpolator endInterpolator) { + mEndInterpolator = endInterpolator; + if (mEndInterpolator == null) { + mEndInterpolator = new LinearInterpolator(); + } + } +} diff --git a/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/indicators/TestPagerIndicator.java b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/indicators/TestPagerIndicator.java new file mode 100644 index 0000000..f3d2dde --- /dev/null +++ b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/indicators/TestPagerIndicator.java @@ -0,0 +1,102 @@ +package net.lucode.hackware.magicindicator.buildins.commonnavigator.indicators; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.RectF; +import android.view.View; + +import net.lucode.hackware.magicindicator.FragmentContainerHelper; +import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IPagerIndicator; +import net.lucode.hackware.magicindicator.buildins.commonnavigator.model.PositionData; + +import java.util.List; + + +/** + * 用于测试的指示器,可用来检测自定义的IMeasurablePagerTitleView是否正确测量内容区域 + * 博客: http://hackware.lucode.net + * Created by hackware on 2016/6/26. + */ +public class TestPagerIndicator extends View implements IPagerIndicator { + private Paint mPaint; + private int mOutRectColor; + private int mInnerRectColor; + private RectF mOutRect = new RectF(); + private RectF mInnerRect = new RectF(); + + private List mPositionDataList; + + public TestPagerIndicator(Context context) { + super(context); + init(context); + } + + private void init(Context context) { + mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mPaint.setStyle(Paint.Style.STROKE); + mOutRectColor = Color.RED; + mInnerRectColor = Color.GREEN; + } + + @Override + protected void onDraw(Canvas canvas) { + mPaint.setColor(mOutRectColor); + canvas.drawRect(mOutRect, mPaint); + mPaint.setColor(mInnerRectColor); + canvas.drawRect(mInnerRect, mPaint); + } + + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + if (mPositionDataList == null || mPositionDataList.isEmpty()) { + return; + } + + // 计算锚点位置 + PositionData current = FragmentContainerHelper.getImitativePositionData(mPositionDataList, position); + PositionData next = FragmentContainerHelper.getImitativePositionData(mPositionDataList, position + 1); + + mOutRect.left = current.mLeft + (next.mLeft - current.mLeft) * positionOffset; + mOutRect.top = current.mTop + (next.mTop - current.mTop) * positionOffset; + mOutRect.right = current.mRight + (next.mRight - current.mRight) * positionOffset; + mOutRect.bottom = current.mBottom + (next.mBottom - current.mBottom) * positionOffset; + + mInnerRect.left = current.mContentLeft + (next.mContentLeft - current.mContentLeft) * positionOffset; + mInnerRect.top = current.mContentTop + (next.mContentTop - current.mContentTop) * positionOffset; + mInnerRect.right = current.mContentRight + (next.mContentRight - current.mContentRight) * positionOffset; + mInnerRect.bottom = current.mContentBottom + (next.mContentBottom - current.mContentBottom) * positionOffset; + + invalidate(); + } + + @Override + public void onPageSelected(int position) { + } + + @Override + public void onPageScrollStateChanged(int state) { + } + + @Override + public void onPositionDataProvide(List dataList) { + mPositionDataList = dataList; + } + + public int getOutRectColor() { + return mOutRectColor; + } + + public void setOutRectColor(int outRectColor) { + mOutRectColor = outRectColor; + } + + public int getInnerRectColor() { + return mInnerRectColor; + } + + public void setInnerRectColor(int innerRectColor) { + mInnerRectColor = innerRectColor; + } +} diff --git a/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/indicators/TriangularPagerIndicator.java b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/indicators/TriangularPagerIndicator.java new file mode 100644 index 0000000..753bed2 --- /dev/null +++ b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/indicators/TriangularPagerIndicator.java @@ -0,0 +1,161 @@ +package net.lucode.hackware.magicindicator.buildins.commonnavigator.indicators; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Path; +import android.view.View; +import android.view.animation.Interpolator; +import android.view.animation.LinearInterpolator; + +import net.lucode.hackware.magicindicator.FragmentContainerHelper; +import net.lucode.hackware.magicindicator.buildins.UIUtil; +import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IPagerIndicator; +import net.lucode.hackware.magicindicator.buildins.commonnavigator.model.PositionData; + +import java.util.List; + +/** + * 带有小尖角的直线指示器 + * 博客: http://hackware.lucode.net + * Created by hackware on 2016/6/26. + */ +public class TriangularPagerIndicator extends View implements IPagerIndicator { + private List mPositionDataList; + private Paint mPaint; + private int mLineHeight; + private int mLineColor; + private int mTriangleHeight; + private int mTriangleWidth; + private boolean mReverse; + private float mYOffset; + + private Path mPath = new Path(); + private Interpolator mStartInterpolator = new LinearInterpolator(); + private float mAnchorX; + + public TriangularPagerIndicator(Context context) { + super(context); + init(context); + } + + private void init(Context context) { + mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mPaint.setStyle(Paint.Style.FILL); + mLineHeight = UIUtil.dip2px(context, 3); + mTriangleWidth = UIUtil.dip2px(context, 14); + mTriangleHeight = UIUtil.dip2px(context, 8); + } + + @Override + protected void onDraw(Canvas canvas) { + mPaint.setColor(mLineColor); + if (mReverse) { + canvas.drawRect(0, getHeight() - mYOffset - mTriangleHeight, getWidth(), getHeight() - mYOffset - mTriangleHeight + mLineHeight, mPaint); + } else { + canvas.drawRect(0, getHeight() - mLineHeight - mYOffset, getWidth(), getHeight() - mYOffset, mPaint); + } + mPath.reset(); + if (mReverse) { + mPath.moveTo(mAnchorX - mTriangleWidth / 2, getHeight() - mYOffset - mTriangleHeight); + mPath.lineTo(mAnchorX, getHeight() - mYOffset); + mPath.lineTo(mAnchorX + mTriangleWidth / 2, getHeight() - mYOffset - mTriangleHeight); + } else { + mPath.moveTo(mAnchorX - mTriangleWidth / 2, getHeight() - mYOffset); + mPath.lineTo(mAnchorX, getHeight() - mTriangleHeight - mYOffset); + mPath.lineTo(mAnchorX + mTriangleWidth / 2, getHeight() - mYOffset); + } + mPath.close(); + canvas.drawPath(mPath, mPaint); + } + + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + if (mPositionDataList == null || mPositionDataList.isEmpty()) { + return; + } + + // 计算锚点位置 + PositionData current = FragmentContainerHelper.getImitativePositionData(mPositionDataList, position); + PositionData next = FragmentContainerHelper.getImitativePositionData(mPositionDataList, position + 1); + + float leftX = current.mLeft + (current.mRight - current.mLeft) / 2; + float rightX = next.mLeft + (next.mRight - next.mLeft) / 2; + + mAnchorX = leftX + (rightX - leftX) * mStartInterpolator.getInterpolation(positionOffset); + + invalidate(); + } + + @Override + public void onPageSelected(int position) { + } + + @Override + public void onPageScrollStateChanged(int state) { + } + + @Override + public void onPositionDataProvide(List dataList) { + mPositionDataList = dataList; + } + + public int getLineHeight() { + return mLineHeight; + } + + public void setLineHeight(int lineHeight) { + mLineHeight = lineHeight; + } + + public int getLineColor() { + return mLineColor; + } + + public void setLineColor(int lineColor) { + mLineColor = lineColor; + } + + public int getTriangleHeight() { + return mTriangleHeight; + } + + public void setTriangleHeight(int triangleHeight) { + mTriangleHeight = triangleHeight; + } + + public int getTriangleWidth() { + return mTriangleWidth; + } + + public void setTriangleWidth(int triangleWidth) { + mTriangleWidth = triangleWidth; + } + + public Interpolator getStartInterpolator() { + return mStartInterpolator; + } + + public void setStartInterpolator(Interpolator startInterpolator) { + mStartInterpolator = startInterpolator; + if (mStartInterpolator == null) { + mStartInterpolator = new LinearInterpolator(); + } + } + + public boolean isReverse() { + return mReverse; + } + + public void setReverse(boolean reverse) { + mReverse = reverse; + } + + public float getYOffset() { + return mYOffset; + } + + public void setYOffset(float yOffset) { + mYOffset = yOffset; + } +} diff --git a/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/indicators/WrapPagerIndicator.java b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/indicators/WrapPagerIndicator.java new file mode 100644 index 0000000..79e23b4 --- /dev/null +++ b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/indicators/WrapPagerIndicator.java @@ -0,0 +1,149 @@ +package net.lucode.hackware.magicindicator.buildins.commonnavigator.indicators; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.RectF; +import android.view.View; +import android.view.animation.Interpolator; +import android.view.animation.LinearInterpolator; + +import net.lucode.hackware.magicindicator.FragmentContainerHelper; +import net.lucode.hackware.magicindicator.buildins.UIUtil; +import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IPagerIndicator; +import net.lucode.hackware.magicindicator.buildins.commonnavigator.model.PositionData; + +import java.util.List; + + +/** + * 包裹住内容区域的指示器,类似天天快报的切换效果,需要和IMeasurablePagerTitleView配合使用 + * 博客: http://hackware.lucode.net + * Created by hackware on 2016/6/26. + */ +public class WrapPagerIndicator extends View implements IPagerIndicator { + private int mVerticalPadding; + private int mHorizontalPadding; + private int mFillColor; + private float mRoundRadius; + private Interpolator mStartInterpolator = new LinearInterpolator(); + private Interpolator mEndInterpolator = new LinearInterpolator(); + + private List mPositionDataList; + private Paint mPaint; + + private RectF mRect = new RectF(); + private boolean mRoundRadiusSet; + + public WrapPagerIndicator(Context context) { + super(context); + init(context); + } + + private void init(Context context) { + mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mPaint.setStyle(Paint.Style.FILL); + mVerticalPadding = UIUtil.dip2px(context, 6); + mHorizontalPadding = UIUtil.dip2px(context, 10); + } + + @Override + protected void onDraw(Canvas canvas) { + mPaint.setColor(mFillColor); + canvas.drawRoundRect(mRect, mRoundRadius, mRoundRadius, mPaint); + } + + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + if (mPositionDataList == null || mPositionDataList.isEmpty()) { + return; + } + + // 计算锚点位置 + PositionData current = FragmentContainerHelper.getImitativePositionData(mPositionDataList, position); + PositionData next = FragmentContainerHelper.getImitativePositionData(mPositionDataList, position + 1); + + mRect.left = current.mContentLeft - mHorizontalPadding + (next.mContentLeft - current.mContentLeft) * mEndInterpolator.getInterpolation(positionOffset); + mRect.top = current.mContentTop - mVerticalPadding; + mRect.right = current.mContentRight + mHorizontalPadding + (next.mContentRight - current.mContentRight) * mStartInterpolator.getInterpolation(positionOffset); + mRect.bottom = current.mContentBottom + mVerticalPadding; + + if (!mRoundRadiusSet) { + mRoundRadius = mRect.height() / 2; + } + + invalidate(); + } + + @Override + public void onPageSelected(int position) { + } + + @Override + public void onPageScrollStateChanged(int state) { + } + + @Override + public void onPositionDataProvide(List dataList) { + mPositionDataList = dataList; + } + + public Paint getPaint() { + return mPaint; + } + + public int getVerticalPadding() { + return mVerticalPadding; + } + + public void setVerticalPadding(int verticalPadding) { + mVerticalPadding = verticalPadding; + } + + public int getHorizontalPadding() { + return mHorizontalPadding; + } + + public void setHorizontalPadding(int horizontalPadding) { + mHorizontalPadding = horizontalPadding; + } + + public int getFillColor() { + return mFillColor; + } + + public void setFillColor(int fillColor) { + mFillColor = fillColor; + } + + public float getRoundRadius() { + return mRoundRadius; + } + + public void setRoundRadius(float roundRadius) { + mRoundRadius = roundRadius; + mRoundRadiusSet = true; + } + + public Interpolator getStartInterpolator() { + return mStartInterpolator; + } + + public void setStartInterpolator(Interpolator startInterpolator) { + mStartInterpolator = startInterpolator; + if (mStartInterpolator == null) { + mStartInterpolator = new LinearInterpolator(); + } + } + + public Interpolator getEndInterpolator() { + return mEndInterpolator; + } + + public void setEndInterpolator(Interpolator endInterpolator) { + mEndInterpolator = endInterpolator; + if (mEndInterpolator == null) { + mEndInterpolator = new LinearInterpolator(); + } + } +} diff --git a/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/model/PositionData.java b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/model/PositionData.java new file mode 100644 index 0000000..66bf88f --- /dev/null +++ b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/model/PositionData.java @@ -0,0 +1,41 @@ +package net.lucode.hackware.magicindicator.buildins.commonnavigator.model; + +/** + * 保存指示器标题的坐标 + * 博客: http://hackware.lucode.net + * Created by hackware on 2016/6/26. + */ +public class PositionData { + public int mLeft; + public int mTop; + public int mRight; + public int mBottom; + public int mContentLeft; + public int mContentTop; + public int mContentRight; + public int mContentBottom; + + public int width() { + return mRight - mLeft; + } + + public int height() { + return mBottom - mTop; + } + + public int contentWidth() { + return mContentRight - mContentLeft; + } + + public int contentHeight() { + return mContentBottom - mContentTop; + } + + public int horizontalCenter() { + return mLeft + width() / 2; + } + + public int verticalCenter() { + return mTop + height() / 2; + } +} diff --git a/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/titles/ClipPagerTitleView.java b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/titles/ClipPagerTitleView.java new file mode 100644 index 0000000..6ecd1a2 --- /dev/null +++ b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/titles/ClipPagerTitleView.java @@ -0,0 +1,191 @@ +package net.lucode.hackware.magicindicator.buildins.commonnavigator.titles; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; +import android.view.View; + +import net.lucode.hackware.magicindicator.buildins.UIUtil; +import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IMeasurablePagerTitleView; + +/** + * 类似今日头条切换效果的指示器标题 + * 博客: http://hackware.lucode.net + * Created by hackware on 2016/6/26. + */ +public class ClipPagerTitleView extends View implements IMeasurablePagerTitleView { + private String mText; + private int mTextColor; + private int mClipColor; + private boolean mLeftToRight; + private float mClipPercent; + + private Paint mPaint; + private Rect mTextBounds = new Rect(); + + public ClipPagerTitleView(Context context) { + super(context); + init(context); + } + + private void init(Context context) { + int textSize = UIUtil.dip2px(context, 16); + mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mPaint.setTextSize(textSize); + int padding = UIUtil.dip2px(context, 10); + setPadding(padding, 0, padding, 0); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + measureTextBounds(); + setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec)); + } + + private int measureWidth(int widthMeasureSpec) { + int mode = MeasureSpec.getMode(widthMeasureSpec); + int size = MeasureSpec.getSize(widthMeasureSpec); + int result = size; + switch (mode) { + case MeasureSpec.AT_MOST: + int width = mTextBounds.width() + getPaddingLeft() + getPaddingRight(); + result = Math.min(width, size); + break; + case MeasureSpec.UNSPECIFIED: + result = mTextBounds.width() + getPaddingLeft() + getPaddingRight(); + break; + default: + break; + } + return result; + } + + private int measureHeight(int heightMeasureSpec) { + int mode = MeasureSpec.getMode(heightMeasureSpec); + int size = MeasureSpec.getSize(heightMeasureSpec); + int result = size; + switch (mode) { + case MeasureSpec.AT_MOST: + int height = mTextBounds.height() + getPaddingTop() + getPaddingBottom(); + result = Math.min(height, size); + break; + case MeasureSpec.UNSPECIFIED: + result = mTextBounds.height() + getPaddingTop() + getPaddingBottom(); + break; + default: + break; + } + return result; + } + + @Override + protected void onDraw(Canvas canvas) { + int x = (getWidth() - mTextBounds.width()) / 2; + Paint.FontMetrics fontMetrics = mPaint.getFontMetrics(); + int y = (int) ((getHeight() - fontMetrics.bottom - fontMetrics.top) / 2); + + // 画底层 + mPaint.setColor(mTextColor); + canvas.drawText(mText, x, y, mPaint); + + // 画clip层 + canvas.save(); + if (mLeftToRight) { + canvas.clipRect(0, 0, getWidth() * mClipPercent, getHeight()); + } else { + canvas.clipRect(getWidth() * (1 - mClipPercent), 0, getWidth(), getHeight()); + } + mPaint.setColor(mClipColor); + canvas.drawText(mText, x, y, mPaint); + canvas.restore(); + } + + @Override + public void onSelected(int index, int totalCount) { + } + + @Override + public void onDeselected(int index, int totalCount) { + } + + @Override + public void onLeave(int index, int totalCount, float leavePercent, boolean leftToRight) { + mLeftToRight = !leftToRight; + mClipPercent = 1.0f - leavePercent; + invalidate(); + } + + @Override + public void onEnter(int index, int totalCount, float enterPercent, boolean leftToRight) { + mLeftToRight = leftToRight; + mClipPercent = enterPercent; + invalidate(); + } + + private void measureTextBounds() { + mPaint.getTextBounds(mText, 0, mText == null ? 0 : mText.length(), mTextBounds); + } + + public String getText() { + return mText; + } + + public void setText(String text) { + mText = text; + requestLayout(); + } + + public float getTextSize() { + return mPaint.getTextSize(); + } + + public void setTextSize(float textSize) { + mPaint.setTextSize(textSize); + requestLayout(); + } + + public int getTextColor() { + return mTextColor; + } + + public void setTextColor(int textColor) { + mTextColor = textColor; + invalidate(); + } + + public int getClipColor() { + return mClipColor; + } + + public void setClipColor(int clipColor) { + mClipColor = clipColor; + invalidate(); + } + + @Override + public int getContentLeft() { + int contentWidth = mTextBounds.width(); + return getLeft() + getWidth() / 2 - contentWidth / 2; + } + + @Override + public int getContentTop() { + Paint.FontMetrics metrics = mPaint.getFontMetrics(); + float contentHeight = metrics.bottom - metrics.top; + return (int) (getHeight() / 2 - contentHeight / 2); + } + + @Override + public int getContentRight() { + int contentWidth = mTextBounds.width(); + return getLeft() + getWidth() / 2 + contentWidth / 2; + } + + @Override + public int getContentBottom() { + Paint.FontMetrics metrics = mPaint.getFontMetrics(); + float contentHeight = metrics.bottom - metrics.top; + return (int) (getHeight() / 2 + contentHeight / 2); + } +} diff --git a/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/titles/ColorTransitionPagerTitleView.java b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/titles/ColorTransitionPagerTitleView.java new file mode 100644 index 0000000..c270619 --- /dev/null +++ b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/titles/ColorTransitionPagerTitleView.java @@ -0,0 +1,38 @@ +package net.lucode.hackware.magicindicator.buildins.commonnavigator.titles; + +import android.content.Context; + +import net.lucode.hackware.magicindicator.buildins.ArgbEvaluatorHolder; + + +/** + * 两种颜色过渡的指示器标题 + * 博客: http://hackware.lucode.net + * Created by hackware on 2016/6/26. + */ +public class ColorTransitionPagerTitleView extends SimplePagerTitleView { + + public ColorTransitionPagerTitleView(Context context) { + super(context); + } + + @Override + public void onLeave(int index, int totalCount, float leavePercent, boolean leftToRight) { + int color = ArgbEvaluatorHolder.eval(leavePercent, mSelectedColor, mNormalColor); + setTextColor(color); + } + + @Override + public void onEnter(int index, int totalCount, float enterPercent, boolean leftToRight) { + int color = ArgbEvaluatorHolder.eval(enterPercent, mNormalColor, mSelectedColor); + setTextColor(color); + } + + @Override + public void onSelected(int index, int totalCount) { + } + + @Override + public void onDeselected(int index, int totalCount) { + } +} diff --git a/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/titles/CommonPagerTitleView.java b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/titles/CommonPagerTitleView.java new file mode 100644 index 0000000..bb6141f --- /dev/null +++ b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/titles/CommonPagerTitleView.java @@ -0,0 +1,143 @@ +package net.lucode.hackware.magicindicator.buildins.commonnavigator.titles; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; + +import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IMeasurablePagerTitleView; + +/** + * 通用的指示器标题,子元素内容由外部提供,事件回传给外部 + * 博客: http://hackware.lucode.net + * Created by hackware on 2016/7/3. + */ +public class CommonPagerTitleView extends FrameLayout implements IMeasurablePagerTitleView { + private OnPagerTitleChangeListener mOnPagerTitleChangeListener; + private ContentPositionDataProvider mContentPositionDataProvider; + + public CommonPagerTitleView(Context context) { + super(context); + } + + @Override + public void onSelected(int index, int totalCount) { + if (mOnPagerTitleChangeListener != null) { + mOnPagerTitleChangeListener.onSelected(index, totalCount); + } + } + + @Override + public void onDeselected(int index, int totalCount) { + if (mOnPagerTitleChangeListener != null) { + mOnPagerTitleChangeListener.onDeselected(index, totalCount); + } + } + + @Override + public void onLeave(int index, int totalCount, float leavePercent, boolean leftToRight) { + if (mOnPagerTitleChangeListener != null) { + mOnPagerTitleChangeListener.onLeave(index, totalCount, leavePercent, leftToRight); + } + } + + @Override + public void onEnter(int index, int totalCount, float enterPercent, boolean leftToRight) { + if (mOnPagerTitleChangeListener != null) { + mOnPagerTitleChangeListener.onEnter(index, totalCount, enterPercent, leftToRight); + } + } + + @Override + public int getContentLeft() { + if (mContentPositionDataProvider != null) { + return mContentPositionDataProvider.getContentLeft(); + } + return getLeft(); + } + + @Override + public int getContentTop() { + if (mContentPositionDataProvider != null) { + return mContentPositionDataProvider.getContentTop(); + } + return getTop(); + } + + @Override + public int getContentRight() { + if (mContentPositionDataProvider != null) { + return mContentPositionDataProvider.getContentRight(); + } + return getRight(); + } + + @Override + public int getContentBottom() { + if (mContentPositionDataProvider != null) { + return mContentPositionDataProvider.getContentBottom(); + } + return getBottom(); + } + + /** + * 外部直接将布局设置进来 + * + * @param contentView + */ + public void setContentView(View contentView) { + setContentView(contentView, null); + } + + public void setContentView(View contentView, FrameLayout.LayoutParams lp) { + removeAllViews(); + if (contentView != null) { + if (lp == null) { + lp = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); + } + addView(contentView, lp); + } + } + + public void setContentView(int layoutId) { + View child = LayoutInflater.from(getContext()).inflate(layoutId, null); + setContentView(child, null); + } + + public OnPagerTitleChangeListener getOnPagerTitleChangeListener() { + return mOnPagerTitleChangeListener; + } + + public void setOnPagerTitleChangeListener(OnPagerTitleChangeListener onPagerTitleChangeListener) { + mOnPagerTitleChangeListener = onPagerTitleChangeListener; + } + + public ContentPositionDataProvider getContentPositionDataProvider() { + return mContentPositionDataProvider; + } + + public void setContentPositionDataProvider(ContentPositionDataProvider contentPositionDataProvider) { + mContentPositionDataProvider = contentPositionDataProvider; + } + + public interface OnPagerTitleChangeListener { + void onSelected(int index, int totalCount); + + void onDeselected(int index, int totalCount); + + void onLeave(int index, int totalCount, float leavePercent, boolean leftToRight); + + void onEnter(int index, int totalCount, float enterPercent, boolean leftToRight); + } + + public interface ContentPositionDataProvider { + int getContentLeft(); + + int getContentTop(); + + int getContentRight(); + + int getContentBottom(); + } +} diff --git a/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/titles/DummyPagerTitleView.java b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/titles/DummyPagerTitleView.java new file mode 100644 index 0000000..f881116 --- /dev/null +++ b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/titles/DummyPagerTitleView.java @@ -0,0 +1,34 @@ +package net.lucode.hackware.magicindicator.buildins.commonnavigator.titles; + +import android.content.Context; +import android.view.View; + +import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IPagerTitleView; + +/** + * 空指示器标题,用于只需要指示器而不需要title的需求 + * 博客: http://hackware.lucode.net + * Created by hackware on 2016/6/26. + */ +public class DummyPagerTitleView extends View implements IPagerTitleView { + + public DummyPagerTitleView(Context context) { + super(context); + } + + @Override + public void onSelected(int index, int totalCount) { + } + + @Override + public void onDeselected(int index, int totalCount) { + } + + @Override + public void onLeave(int index, int totalCount, float leavePercent, boolean leftToRight) { + } + + @Override + public void onEnter(int index, int totalCount, float enterPercent, boolean leftToRight) { + } +} diff --git a/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/titles/SimplePagerTitleView.java b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/titles/SimplePagerTitleView.java new file mode 100644 index 0000000..a5a1847 --- /dev/null +++ b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/titles/SimplePagerTitleView.java @@ -0,0 +1,118 @@ +package net.lucode.hackware.magicindicator.buildins.commonnavigator.titles; + +import android.content.Context; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.Typeface; +import android.text.TextUtils; +import android.view.Gravity; +import android.widget.TextView; + +import net.lucode.hackware.magicindicator.buildins.UIUtil; +import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IMeasurablePagerTitleView; + + +/** + * 带文本的指示器标题 + * 博客: http://hackware.lucode.net + * Created by hackware on 2016/6/26. + */ +public class SimplePagerTitleView extends TextView implements IMeasurablePagerTitleView { + protected int mSelectedColor; + protected int mNormalColor; + + public SimplePagerTitleView(Context context) { + super(context, null); + init(context); + } + + private void init(Context context) { + setGravity(Gravity.CENTER); + int padding = UIUtil.dip2px(context, 5); + setPadding(padding, 0, padding, 0); + setSingleLine(); + setEllipsize(TextUtils.TruncateAt.END); + } + + @Override + public void onSelected(int index, int totalCount) { + setTextColor(mSelectedColor); + } + + @Override + public void onDeselected(int index, int totalCount) { + setTextColor(mNormalColor); + } + + @Override + public void onLeave(int index, int totalCount, float leavePercent, boolean leftToRight) { + } + + @Override + public void onEnter(int index, int totalCount, float enterPercent, boolean leftToRight) { + } + + @Override + public int getContentLeft() { + Rect bound = new Rect(); + String longestString = ""; + if (getText().toString().contains("\n")) { + String[] brokenStrings = getText().toString().split("\\n"); + for (String each : brokenStrings) { + if (each.length() > longestString.length()) longestString = each; + } + } else { + longestString = getText().toString(); + } + getPaint().getTextBounds(longestString, 0, longestString.length(), bound); + int contentWidth = bound.width(); + return getLeft() + getWidth() / 2 - contentWidth / 2; + } + + @Override + public int getContentTop() { + Paint.FontMetrics metrics = getPaint().getFontMetrics(); + float contentHeight = metrics.bottom - metrics.top; + return (int) (getHeight() / 2 - contentHeight / 2); + } + + @Override + public int getContentRight() { + Rect bound = new Rect(); + String longestString = ""; + if (getText().toString().contains("\n")) { + String[] brokenStrings = getText().toString().split("\\n"); + for (String each : brokenStrings) { + if (each.length() > longestString.length()) longestString = each; + } + } else { + longestString = getText().toString(); + } + getPaint().getTextBounds(longestString, 0, longestString.length(), bound); + int contentWidth = bound.width(); + return getLeft() + getWidth() / 2 + contentWidth / 2; + } + + @Override + public int getContentBottom() { + Paint.FontMetrics metrics = getPaint().getFontMetrics(); + float contentHeight = metrics.bottom - metrics.top; + return (int) (getHeight() / 2 + contentHeight / 2); + } + + public int getSelectedColor() { + return mSelectedColor; + } + + public void setSelectedColor(int selectedColor) { + mSelectedColor = selectedColor; + } + + public int getNormalColor() { + return mNormalColor; + } + + public void setNormalColor(int normalColor) { + mNormalColor = normalColor; + } +} diff --git a/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/titles/badge/BadgeAnchor.java b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/titles/badge/BadgeAnchor.java new file mode 100644 index 0000000..b708d90 --- /dev/null +++ b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/titles/badge/BadgeAnchor.java @@ -0,0 +1,22 @@ +package net.lucode.hackware.magicindicator.buildins.commonnavigator.titles.badge; + +/** + * 角标的锚点 + * Created by hackware on 2016/7/19. + */ +public enum BadgeAnchor { + LEFT, + TOP, + RIGHT, + BOTTOM, + CONTENT_LEFT, + CONTENT_TOP, + CONTENT_RIGHT, + CONTENT_BOTTOM, + CENTER_X, + CENTER_Y, + LEFT_EDGE_CENTER_X, + TOP_EDGE_CENTER_Y, + RIGHT_EDGE_CENTER_X, + BOTTOM_EDGE_CENTER_Y +} diff --git a/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/titles/badge/BadgePagerTitleView.java b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/titles/badge/BadgePagerTitleView.java new file mode 100644 index 0000000..b45483d --- /dev/null +++ b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/titles/badge/BadgePagerTitleView.java @@ -0,0 +1,221 @@ +package net.lucode.hackware.magicindicator.buildins.commonnavigator.titles.badge; + +import android.content.Context; +import android.view.View; +import android.widget.FrameLayout; + +import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IMeasurablePagerTitleView; +import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IPagerTitleView; + +/** + * 支持显示角标的title,角标布局可自定义 + * 博客: http://hackware.lucode.net + * Created by hackware on 2016/7/18. + */ +public class BadgePagerTitleView extends FrameLayout implements IMeasurablePagerTitleView { + private IPagerTitleView mInnerPagerTitleView; + private View mBadgeView; + private boolean mAutoCancelBadge = true; + + private BadgeRule mXBadgeRule; + private BadgeRule mYBadgeRule; + + public BadgePagerTitleView(Context context) { + super(context); + } + + @Override + public void onSelected(int index, int totalCount) { + if (mInnerPagerTitleView != null) { + mInnerPagerTitleView.onSelected(index, totalCount); + } + if (mAutoCancelBadge) { + setBadgeView(null); + } + } + + @Override + public void onDeselected(int index, int totalCount) { + if (mInnerPagerTitleView != null) { + mInnerPagerTitleView.onDeselected(index, totalCount); + } + } + + @Override + public void onLeave(int index, int totalCount, float leavePercent, boolean leftToRight) { + if (mInnerPagerTitleView != null) { + mInnerPagerTitleView.onLeave(index, totalCount, leavePercent, leftToRight); + } + } + + @Override + public void onEnter(int index, int totalCount, float enterPercent, boolean leftToRight) { + if (mInnerPagerTitleView != null) { + mInnerPagerTitleView.onEnter(index, totalCount, enterPercent, leftToRight); + } + } + + public IPagerTitleView getInnerPagerTitleView() { + return mInnerPagerTitleView; + } + + public void setInnerPagerTitleView(IPagerTitleView innerPagerTitleView) { + if (mInnerPagerTitleView == innerPagerTitleView) { + return; + } + mInnerPagerTitleView = innerPagerTitleView; + removeAllViews(); + if (mInnerPagerTitleView instanceof View) { + FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT); + addView((View) mInnerPagerTitleView, lp); + } + if (mBadgeView != null) { + FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); + addView(mBadgeView, lp); + } + } + + public View getBadgeView() { + return mBadgeView; + } + + public void setBadgeView(View badgeView) { + if (mBadgeView == badgeView) { + return; + } + mBadgeView = badgeView; + removeAllViews(); + if (mInnerPagerTitleView instanceof View) { + FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT); + addView((View) mInnerPagerTitleView, lp); + } + if (mBadgeView != null) { + FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); + addView(mBadgeView, lp); + } + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + if (mInnerPagerTitleView instanceof View && mBadgeView != null) { + int[] position = new int[14]; // 14种角标定位方式 + View v = (View) mInnerPagerTitleView; + position[0] = v.getLeft(); + position[1] = v.getTop(); + position[2] = v.getRight(); + position[3] = v.getBottom(); + if (mInnerPagerTitleView instanceof IMeasurablePagerTitleView) { + IMeasurablePagerTitleView view = (IMeasurablePagerTitleView) mInnerPagerTitleView; + position[4] = view.getContentLeft(); + position[5] = view.getContentTop(); + position[6] = view.getContentRight(); + position[7] = view.getContentBottom(); + } else { + for (int i = 4; i < 8; i++) { + position[i] = position[i - 4]; + } + } + position[8] = v.getWidth() / 2; + position[9] = v.getHeight() / 2; + position[10] = position[4] / 2; + position[11] = position[5] / 2; + position[12] = position[6] + (position[2] - position[6]) / 2; + position[13] = position[7] + (position[3] - position[7]) / 2; + + // 根据设置的BadgeRule调整角标的位置 + if (mXBadgeRule != null) { + int x = position[mXBadgeRule.getAnchor().ordinal()]; + int offset = mXBadgeRule.getOffset(); + int newLeft = x + offset; + mBadgeView.offsetLeftAndRight(newLeft - mBadgeView.getLeft()); + } + if (mYBadgeRule != null) { + int y = position[mYBadgeRule.getAnchor().ordinal()]; + int offset = mYBadgeRule.getOffset(); + int newTop = y + offset; + mBadgeView.offsetTopAndBottom(newTop - mBadgeView.getTop()); + } + } + } + + @Override + public int getContentLeft() { + if (mInnerPagerTitleView instanceof IMeasurablePagerTitleView) { + return getLeft() + ((IMeasurablePagerTitleView) mInnerPagerTitleView).getContentLeft(); + } + return getLeft(); + } + + @Override + public int getContentTop() { + if (mInnerPagerTitleView instanceof IMeasurablePagerTitleView) { + return ((IMeasurablePagerTitleView) mInnerPagerTitleView).getContentTop(); + } + return getTop(); + } + + @Override + public int getContentRight() { + if (mInnerPagerTitleView instanceof IMeasurablePagerTitleView) { + return getLeft() + ((IMeasurablePagerTitleView) mInnerPagerTitleView).getContentRight(); + } + return getRight(); + } + + @Override + public int getContentBottom() { + if (mInnerPagerTitleView instanceof IMeasurablePagerTitleView) { + return ((IMeasurablePagerTitleView) mInnerPagerTitleView).getContentBottom(); + } + return getBottom(); + } + + public BadgeRule getXBadgeRule() { + return mXBadgeRule; + } + + public void setXBadgeRule(BadgeRule badgeRule) { + if (badgeRule != null) { + BadgeAnchor anchor = badgeRule.getAnchor(); + if (anchor != BadgeAnchor.LEFT + && anchor != BadgeAnchor.RIGHT + && anchor != BadgeAnchor.CONTENT_LEFT + && anchor != BadgeAnchor.CONTENT_RIGHT + && anchor != BadgeAnchor.CENTER_X + && anchor != BadgeAnchor.LEFT_EDGE_CENTER_X + && anchor != BadgeAnchor.RIGHT_EDGE_CENTER_X) { + throw new IllegalArgumentException("x badge rule is wrong."); + } + } + mXBadgeRule = badgeRule; + } + + public BadgeRule getYBadgeRule() { + return mYBadgeRule; + } + + public void setYBadgeRule(BadgeRule badgeRule) { + if (badgeRule != null) { + BadgeAnchor anchor = badgeRule.getAnchor(); + if (anchor != BadgeAnchor.TOP + && anchor != BadgeAnchor.BOTTOM + && anchor != BadgeAnchor.CONTENT_TOP + && anchor != BadgeAnchor.CONTENT_BOTTOM + && anchor != BadgeAnchor.CENTER_Y + && anchor != BadgeAnchor.TOP_EDGE_CENTER_Y + && anchor != BadgeAnchor.BOTTOM_EDGE_CENTER_Y) { + throw new IllegalArgumentException("y badge rule is wrong."); + } + } + mYBadgeRule = badgeRule; + } + + public boolean isAutoCancelBadge() { + return mAutoCancelBadge; + } + + public void setAutoCancelBadge(boolean autoCancelBadge) { + mAutoCancelBadge = autoCancelBadge; + } +} diff --git a/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/titles/badge/BadgeRule.java b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/titles/badge/BadgeRule.java new file mode 100644 index 0000000..6cd10d1 --- /dev/null +++ b/magicindicator/src/main/java/net/lucode/hackware/magicindicator/buildins/commonnavigator/titles/badge/BadgeRule.java @@ -0,0 +1,31 @@ +package net.lucode.hackware.magicindicator.buildins.commonnavigator.titles.badge; + +/** + * 角标的定位规则 + * Created by hackware on 2016/7/19. + */ +public class BadgeRule { + private BadgeAnchor mAnchor; + private int mOffset; + + public BadgeRule(BadgeAnchor anchor, int offset) { + mAnchor = anchor; + mOffset = offset; + } + + public BadgeAnchor getAnchor() { + return mAnchor; + } + + public void setAnchor(BadgeAnchor anchor) { + mAnchor = anchor; + } + + public int getOffset() { + return mOffset; + } + + public void setOffset(int offset) { + mOffset = offset; + } +} diff --git a/magicindicator/src/main/res/layout/pager_navigator_layout.xml b/magicindicator/src/main/res/layout/pager_navigator_layout.xml new file mode 100644 index 0000000..c2cb618 --- /dev/null +++ b/magicindicator/src/main/res/layout/pager_navigator_layout.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/magicindicator/src/main/res/layout/pager_navigator_layout_no_scroll.xml b/magicindicator/src/main/res/layout/pager_navigator_layout_no_scroll.xml new file mode 100644 index 0000000..3eef19d --- /dev/null +++ b/magicindicator/src/main/res/layout/pager_navigator_layout_no_scroll.xml @@ -0,0 +1,18 @@ + + + + + + + + \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 7ffbeb3..fd876b5 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -include ':app', ':customer', ':base', ':device', ':http', ':media', ':messaging', ':storage', ':uikit', ':location', ':citypicker', ':share', ':yunxinkit', ':faceunity' +include ':app', ':customer', ':base', ':device', ':http', ':media', ':messaging', ':storage', ':uikit', ':location', ':citypicker', ':share', ':yunxinkit', ':faceunity',':magicindicator' diff --git a/uikit/src/main/res/layout/bottom_tab1.xml b/uikit/src/main/res/layout/bottom_tab1.xml index 855d7fb..d262a14 100644 --- a/uikit/src/main/res/layout/bottom_tab1.xml +++ b/uikit/src/main/res/layout/bottom_tab1.xml @@ -81,6 +81,7 @@