Библиотека Header2ActionBar для Android
(демо для привлечения внимания)
Вы, наверное, уже видели похожее в приложениях от Google (Play Музыка, Google Пресса) и, возможно, каких-либо других. Для этих целей уже довольно давно существует библиотека от ManuelPeinado — FadingActionBar, которая прекрасно выполняет свою задачу, но к сожалению, имеет два «фатальных» недостатка.
Второй из них описан как известная проблема:
Known Issues
There is an important issue with the library and ListViews. More specifically, things don’t work quite right when the activity is re-created due to a configuration change. So, unless you handle configuration changes yourself (or your activity is portrait/landscape only), I strongly suggest you stick to having your content in a ScrollView until a solution to this issue is found.
Стараясь исправить этот недостаток, я решил написать свою реализацию, тем самым устранив и оба недостатка 🙂
Библиотека состоит из трёх файлов:
/** * Created by AChep@xda <artemchep@gmail.com> */ public class FadingActionBarActivity extends Activity { private static final String TAG = "FadingActionBarActivity"; private int mAlpha = 255; private Drawable mDrawable; private boolean isAlphaLocked; public void setActionBarBackgroundDrawable(Drawable drawable) { getActionBar().setBackgroundDrawable(drawable); mDrawable = drawable; if (mAlpha == 255) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) mAlpha = drawable.getAlpha(); } else { setActionBarAlpha(mAlpha); } } /** * An {@link android.app.ActionBar} background drawable. * * @see #setActionBarBackgroundDrawable(android.graphics.drawable.Drawable) * @see #setActionBarAlpha(int) */ public Drawable getActionBarBackgroundDrawable() { return mDrawable; } /** * Please use this method for global changes only! * Otherwise, please, use {@link android.graphics.drawable.Drawable#setAlpha(int)} * to {@link #getActionBarBackgroundDrawable()} directly. * * @param alpha a value from 0 to 255 * @see #getActionBarBackgroundDrawable() * @see #getActionBarAlpha() */ public void setActionBarAlpha(int alpha) { if (mDrawable == null) { Log.w(TAG, "Set action bar background before setting alpha!"); return; } if (!isAlphaLocked) mDrawable.setAlpha(alpha); mAlpha = alpha; } public int getActionBarAlpha() { return mAlpha; } public void setActionBarAlphaLocked(boolean isLocked) { isAlphaLocked = isLocked; } }
/** * Little header fragment. * <p> * Created by AChep@xda <artemchep@gmail.com> * </p> */ public class HeaderFragment extends Fragment { private static final String TAG = "HeaderFragment"; private View mHeader; private int mHeaderHeight; private int mCurrentHeaderHeight; private int mCurrentHeaderTranslateY; private OnHeaderScrollChangeListener mOnHeaderScrollChangeListener; public interface OnHeaderScrollChangeListener { public void onHeaderScrollChanged(float progress, int height, int scroll); } public void setOnHeaderScrollChangeListener(OnHeaderScrollChangeListener listener) { mOnHeaderScrollChangeListener = listener; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { final Activity activity = getActivity(); mHeader = inflater.inflate(getHeaderResource(), container, false); mHeaderHeight = mHeader.getLayoutParams().height; mCurrentHeaderHeight = mHeaderHeight; mCurrentHeaderTranslateY = 0; onPrepareHeaderView(mHeader); View content = inflater.inflate(getContentResource(), container, false); assert content != null; if (content instanceof ListView) { final ListView listView = (ListView) content; // Perform fake header view. final Space listFakeHeader = new Space(activity); listFakeHeader.setLayoutParams(new ListView.LayoutParams( 0, mHeaderHeight)); onPrepareContentListView(listView); listView.addHeaderView(listFakeHeader); listView.setOnScrollListener(new AbsListView.OnScrollListener() { @Override public void onScrollStateChanged(AbsListView absListView, int i) { /* unused */ } @Override public void onScroll(AbsListView absListView, int i, int i2, int i3) { final View child = absListView.getChildAt(0); if (child == listFakeHeader) { updateHeaderScroll(child.getTop()); } else { updateHeaderScroll(-mHeaderHeight); } } }); } else { onPrepareContentView(content); final NotifyingScrollView scrollView = new NotifyingScrollView(activity); scrollView.addView(content); scrollView.setOnScrollChangedListener(new NotifyingScrollView.OnScrollChangedListener() { @Override public void onScrollChanged(ScrollView who, int l, int t, int oldl, int oldt) { updateHeaderScroll(-t); } }); content = scrollView; } final FrameLayout root = new FrameLayout(activity); root.addView(content, new FrameLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); root.addView(mHeader); return root; } private void updateHeaderScroll(int scrollTo) { scrollTo = scrollTo > 0 ? 0 : scrollTo < -mHeaderHeight ? mHeaderHeight : scrollTo; final boolean allowChangeHeight = isHeaderHeightFloating(); final int height = mHeaderHeight + scrollTo / 2; final int transY = allowChangeHeight ? scrollTo / 2 : scrollTo; if (height != mCurrentHeaderHeight && allowChangeHeight) { final ViewGroup.LayoutParams lp = mHeader.getLayoutParams(); lp.height = height; mHeader.setLayoutParams(lp); mCurrentHeaderHeight = height; } if (transY != mCurrentHeaderTranslateY) { mHeader.setTranslationY(transY); mCurrentHeaderTranslateY = transY; if (mOnHeaderScrollChangeListener != null) { // Notify upper fragment to update ActionBar's alpha or whatever. int scroll = Math.abs(scrollTo); mOnHeaderScrollChangeListener.onHeaderScrollChanged( (float) scroll / mHeaderHeight, mHeaderHeight, scroll); } } } /** * If true, header's height might be changed on scroll. * <p>Note: It takes a lot of calculations to measure the header all the time.</p> */ public boolean isHeaderHeightFloating() { return false; } /** * Int reference to header's resource. * * @see #onPrepareHeaderView(android.view.View) * @see #getContentResource() */ public int getHeaderResource() { return 0; } /** * This is the place for setting up the header. * * @param view inflated header view. * @see #getHeaderResource() */ public void onPrepareHeaderView(View view) { /* for my child */ } /** * Int reference to content's resource. * <p> * <b>Attention</b>: Parent view must be {@link android.widget.ListView ListView} * or something else which will work inside of {@link android.widget.ScrollView ScrollView}. * Otherwise it <b>WON'T</b> work. * </p> * * @see #getHeaderResource() * @see #onPrepareContentListView(ListView) */ public int getContentResource() { return 0; } /** * Called if the content's parent is a {@link android.widget.ListView ListView}. * * @see #getContentResource() */ public void onPrepareContentListView(ListView listView) { /* for my child */ } /** * Called if the content's parent is NOT a {@link android.widget.ListView ListView}. * * @see #getContentResource() */ public void onPrepareContentView(View view) { /* for my child */ } }
/** * @author Cyril Mottier with modifications from Manuel Peinado */ public class NotifyingScrollView extends ScrollView { // Edge-effects don't mix well with the translucent action bar in Android 2.X private boolean mDisableEdgeEffects = true; /** * @author Cyril Mottier */ public interface OnScrollChangedListener { void onScrollChanged(ScrollView who, int l, int t, int oldl, int oldt); } private OnScrollChangedListener mOnScrollChangedListener; public NotifyingScrollView(Context context) { super(context); } public NotifyingScrollView(Context context, AttributeSet attrs) { super(context, attrs); } public NotifyingScrollView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); if (mOnScrollChangedListener != null) { mOnScrollChangedListener.onScrollChanged(this, l, t, oldl, oldt); } } public void setOnScrollChangedListener(OnScrollChangedListener listener) { mOnScrollChangedListener = listener; } @Override protected float getTopFadingEdgeStrength() { // http://stackoverflow.com/a/6894270/244576 if (mDisableEdgeEffects && Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { return 0.0f; } return super.getTopFadingEdgeStrength(); } @Override protected float getBottomFadingEdgeStrength() { // http://stackoverflow.com/a/6894270/244576 if (mDisableEdgeEffects && Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { return 0.0f; } return super.getBottomFadingEdgeStrength(); } }
и лежит на GitHub‘е как проект библиотеки созданной в Android Studio.
Использование
HeaderFragment и FadingActionBarActivity наследуются от нативных собратьев, так что пока о Android < 4.0 можно забыть.
Наше приложение будет подобием демо на скриншоте сверху. Итак, пример Activity:
public class MainActivity extends FadingActionBarActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Инициализация фона ActionBar'a setActionBarBackgroundDrawable(getResources().getDrawable(R.drawable.actionbar_bg)); FragmentManager fragmentManager = getFragmentManager(); fragmentManager.beginTransaction() .replace(R.id.container, new TestHeaderFragment() ).commit(); } }
public class TestHeaderFragment extends HeaderFragment { @Override public void onAttach(Activity activity) { super.onAttach(activity); // Меняем прозрачность ActionBar'a во время скроллинга setOnHeaderScrollChangeListener(new OnHeaderScrollChangeListener() { @Override public void onHeaderScrollChanged(float progress, int height, int scroll) { height -= getActivity().getActionBar().getHeight(); progress = (float) scroll / height; if (progress > 1f) progress = 1f; ((FadingActionBarActivity) getActivity()).setActionBarAlpha((int) (255 * progress)); } }); } @Override public int getHeaderResource() { return R.layout.header; } @Override public void onPrepareHeaderView(View view) { super.onPrepareHeaderView(view); // Загружаем view контентом } @Override public int getContentResource() { return R.layout.content; } @Override public void onPrepareContentListView(ListView listView) { super.onPrepareContentListView(listView); // Загружаем view контентом listView.setAdapter(new ArrayAdapter<>(getActivity(), android.R.layout.simple_list_item_1, android.R.id.title, new String[]{"Android", "Android", "Android", "Android", "Android", "Android", "Android", "Android", "Android", "Android", "Android"})); }
Я заранее закрываю свое лицо руками и прошу прощения за свой код и английский. 🙁
ссылка на оригинал статьи http://habrahabr.ru/post/204940/
Добавить комментарий