Java tutorial
/* * Copyright 2015 JP Ventura * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.jpventura.xyzreader.ui; import android.app.Fragment; import android.app.LoaderManager; import android.content.Intent; import android.content.Loader; import android.content.res.ColorStateList; import android.database.Cursor; import android.graphics.Color; import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; import android.os.Bundle; import android.support.design.widget.FloatingActionButton; import android.support.v4.app.ShareCompat; import android.support.v7.graphics.Palette; import android.support.v7.graphics.Palette.PaletteAsyncListener; import android.text.Html; import android.text.format.DateUtils; import android.text.method.LinkMovementMethod; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; import com.jpventura.xyzreader.R; import com.jpventura.xyzreader.data.ArticleLoader; import com.squareup.picasso.Picasso; import com.squareup.picasso.Callback; /** * A fragment representing a single Article detail screen. This fragment is * either contained in a {@link ArticleListActivity} in two-pane mode (on * tablets) or a {@link ArticleDetailActivity} on handsets. */ public class ArticleDetailFragment extends Fragment implements Callback, LoaderManager.LoaderCallbacks<Cursor>, PaletteAsyncListener { private static final String TAG = "ArticleDetailFragment"; public static final String ARG_ITEM_ID = "item_id"; private static final float PARALLAX_FACTOR = 1.25f; private Cursor mCursor; private long mItemId; private View mRootView; private int mMutedColor = 0xFF333333; private ObservableScrollView mScrollView; private DrawInsetsFrameLayout mDrawInsetsFrameLayout; private ColorDrawable mStatusBarColorDrawable; private int mTopInset; private View mPhotoContainerView; private ImageView mPhotoView; private int mScrollY; private boolean mIsCard = false; private int mStatusBarFullOpacityBottom; private TextView mTitleView; private TextView mSubitleView; private TextView mBodyView; /** * Mandatory empty constructor for the fragment manager to instantiate the * fragment (e.g. upon screen orientation changes). */ public ArticleDetailFragment() { } public static ArticleDetailFragment newInstance(long itemId) { Bundle arguments = new Bundle(); arguments.putLong(ARG_ITEM_ID, itemId); ArticleDetailFragment fragment = new ArticleDetailFragment(); fragment.setArguments(arguments); return fragment; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (getArguments().containsKey(ARG_ITEM_ID)) { mItemId = getArguments().getLong(ARG_ITEM_ID); } mIsCard = getResources().getBoolean(R.bool.detail_is_card); mStatusBarFullOpacityBottom = getResources().getDimensionPixelSize(R.dimen.detail_card_top_margin); setHasOptionsMenu(true); } public ArticleDetailActivity getActivityCast() { return (ArticleDetailActivity) getActivity(); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); // In support library r8, calling initLoader for a fragment in a FragmentPagerAdapter in // the fragment's onCreate may cause the same LoaderManager to be dealt to multiple // fragments because their mIndex is -1 (haven't been added to the activity yet). Thus, // we do this in onActivityCreated. getLoaderManager().initLoader(0, null, this); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { mRootView = inflater.inflate(R.layout.fragment_article_detail, container, false); mDrawInsetsFrameLayout = (DrawInsetsFrameLayout) mRootView.findViewById(R.id.draw_insets_frame_layout); mDrawInsetsFrameLayout.setOnInsetsCallback(new DrawInsetsFrameLayout.OnInsetsCallback() { @Override public void onInsetsChanged(Rect insets) { mTopInset = insets.top; } }); mScrollView = (ObservableScrollView) mRootView.findViewById(R.id.scrollview); mScrollView.setCallbacks(new ObservableScrollView.Callbacks() { @Override public void onScrollChanged() { mScrollY = mScrollView.getScrollY(); getActivityCast().onUpButtonFloorChanged(mItemId, ArticleDetailFragment.this); mPhotoContainerView.setTranslationY((int) (mScrollY - mScrollY / PARALLAX_FACTOR)); updateStatusBar(); } }); mPhotoView = (ImageView) mRootView.findViewById(R.id.photo); mPhotoContainerView = mRootView.findViewById(R.id.photo_container); mStatusBarColorDrawable = new ColorDrawable(0); mRootView.findViewById(R.id.share_fab).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { startActivity( Intent.createChooser( ShareCompat.IntentBuilder.from(getActivity()).setType("text/plain") .setText("Some sample text").getIntent(), getString(R.string.action_share))); } }); bindViews(); updateStatusBar(); return mRootView; } private void updateStatusBar() { int color = 0; if (mPhotoView != null && mTopInset != 0 && mScrollY > 0) { float f = progress(mScrollY, mStatusBarFullOpacityBottom - mTopInset * 3, mStatusBarFullOpacityBottom - mTopInset); color = Color.argb((int) (255 * f), (int) (Color.red(mMutedColor) * 0.9), (int) (Color.green(mMutedColor) * 0.9), (int) (Color.blue(mMutedColor) * 0.9)); } mStatusBarColorDrawable.setColor(color); mDrawInsetsFrameLayout.setInsetBackground(mStatusBarColorDrawable); } static float progress(float v, float min, float max) { return constrain((v - min) / (max - min), 0, 1); } static float constrain(float val, float min, float max) { if (val < min) { return min; } else if (val > max) { return max; } else { return val; } } private void bindViews() { if (mRootView == null) { return; } mTitleView = (TextView) mRootView.findViewById(R.id.article_title); mSubitleView = (TextView) mRootView.findViewById(R.id.article_byline); mSubitleView.setMovementMethod(new LinkMovementMethod()); mBodyView = (TextView) mRootView.findViewById(R.id.article_body); if (null == mCursor) { mRootView.setVisibility(View.GONE); mTitleView.setText("N/A"); mSubitleView.setText("N/A"); mBodyView.setText("N/A"); return; } mRootView.setAlpha(0); mRootView.setVisibility(View.VISIBLE); mRootView.animate().alpha(1); mTitleView.setText(mCursor.getString(ArticleLoader.Query.TITLE)); mSubitleView.setText(Html.fromHtml(DateUtils .getRelativeTimeSpanString(mCursor.getLong(ArticleLoader.Query.PUBLISHED_DATE), System.currentTimeMillis(), DateUtils.HOUR_IN_MILLIS, DateUtils.FORMAT_ABBREV_ALL) .toString() + " by <font color='#ffffff'>" + mCursor.getString(ArticleLoader.Query.AUTHOR) + "</font>")); mBodyView.setText(Html.fromHtml(mCursor.getString(ArticleLoader.Query.BODY))); Picasso.with(getActivity()).load(mCursor.getString(ArticleLoader.Query.PHOTO_URL)).into(mPhotoView, this); } @Override public Loader<Cursor> onCreateLoader(int i, Bundle bundle) { return ArticleLoader.newInstanceForItemId(getActivity(), mItemId); } @Override public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) { if (!isAdded() && (null != cursor)) { cursor.close(); return; } mCursor = cursor; if (mCursor != null && !mCursor.moveToFirst()) { Log.e(TAG, "Error reading item detail cursor"); mCursor.close(); mCursor = null; } bindViews(); } @Override public void onLoaderReset(Loader<Cursor> cursorLoader) { mCursor = null; bindViews(); } @Override public void onError() { } @Override public void onGenerated(Palette palette) { int primaryColor = mRootView.getResources().getColor(R.color.primary); int primaryDarkColor = mRootView.getResources().getColor(R.color.primary_dark); int darkMutedColor = palette.getDarkMutedColor(primaryDarkColor); FloatingActionButton fab = (FloatingActionButton) mRootView.findViewById(R.id.share_fab); int lightVibrantColor = palette.getLightVibrantColor(getResources().getColor(android.R.color.white)); int lightMutedColor = palette.getLightVibrantColor(getResources().getColor(R.color.text_primary)); int vibrantColor = palette.getVibrantColor(getResources().getColor(R.color.accent)); fab.setRippleColor(lightVibrantColor); fab.setBackgroundTintList(ColorStateList.valueOf(vibrantColor)); mRootView.findViewById(R.id.meta_bar).setBackgroundColor(darkMutedColor); mTitleView.setTextColor(lightMutedColor); mBodyView.setLinkTextColor(vibrantColor); mMutedColor = palette.getDarkMutedColor(0xff333333); updateStatusBar(); } @Override public void onSuccess() { Palette.from(((BitmapDrawable) mPhotoView.getDrawable()).getBitmap()).generate(this); } public int getUpButtonFloor() { if (mPhotoContainerView == null || mPhotoView.getHeight() == 0) { return Integer.MAX_VALUE; } // account for parallax return mIsCard ? (int) mPhotoContainerView.getTranslationY() + mPhotoView.getHeight() - mScrollY : mPhotoView.getHeight() - mScrollY; } }