Was it Frag-Ment to be?
Luke Sleeman - Freelance Android developer
http://lukesleeman.com luke.sleeman@gmail.com @LukeSleeman
Was it Frag-Ment to be? Luke Sleeman - Freelance Android developer - - PowerPoint PPT Presentation
Was it Frag-Ment to be? Luke Sleeman - Freelance Android developer http://lukesleeman.com luke.sleeman@gmail.com @LukeSleeman Was it Frag-Ment to be? Intro - What are fragments? History Problems with fragments - Lifecycle, Bugs, Hard
Luke Sleeman - Freelance Android developer
http://lukesleeman.com luke.sleeman@gmail.com @LukeSleeman
fragments, alternatives
any point in time.
Activity List Fragment Detail Fragment
? ? ???
private void updateTimeStamps(){ ... String format = getContext().getString(R.string…) ... } private void newDataReceived(List<> ...){ ... updateTimeStamps(); ... } private void setTimeZone(TimeZone timeZone){ ... updateTimeStamps(); ... } public void startDataRefresh(){ ... setTimeZone(TimeZone.getDefault()); ... }
private void updateTimeStamps(){ ... String format = getContext().getString(R.string…) ... } private void newDataReceived(List<> ...){ ... updateTimeStamps(); ... } private void setTimeZone(TimeZone timeZone){ ... updateTimeStamps(); ... } public void startDataRefresh(){ ... setTimeZone(TimeZone.getDefault()); ... }
private void updateTimeStamps(){ ... String format = getContext().getString(R.string…) ... } private void newDataReceived(List<> ...){ ... updateTimeStamps(); ... } private void setTimeZone(TimeZone timeZone){ ... updateTimeStamps(); ... } public void startDataRefresh(){ ... setTimeZone(TimeZone.getDefault()); ... }
private void updateTimeStamps(){ ... String format = getContext().getString(R.string…) ... } private void newDataReceived(List<> ...){ ... updateTimeStamps(); ... } private void setTimeZone(TimeZone timeZone){ ... updateTimeStamps(); ... } public void startDataRefresh(){ ... setTimeZone(TimeZone.getDefault()); ... }
people”
things work completely differently from how you expect”
bugs didn’t help!”
be a bad bit of syntactic sugar.
UIs?
1 - Don’t use them! 2 - Mitigate the risks if you do use them
(Subclass LinearLayout or FrameLayout)
public class UserSummaryView extends LinearLayout { private ImageView profileImage; private TextView name; private TextView title; private TextView bio; public UserSummaryView(Context context) { super(context); init(context); } public UserSummaryView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public UserSummaryView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); }
public class UserSummaryView extends LinearLayout { private ImageView profileImage; private TextView name; private TextView title; private TextView bio; public UserSummaryView(Context context) { super(context); init(context); } public UserSummaryView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public UserSummaryView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); }
public class UserSummaryView extends LinearLayout { private ImageView profileImage; private TextView name; private TextView title; private TextView bio; public UserSummaryView(Context context) { super(context); init(context); } public UserSummaryView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public UserSummaryView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); }
private void init(Context context){
setOrientation(VERTICAL);
LayoutInflater.from(context).inflate( R.layout.view_user_summary, this); profileImage = (ImageView)findViewById(R.id.user_summary_image); name = (TextView)findViewById(R.id.user_summary_name); title = (TextView)findViewById(R.id.user_summary_title); bio = (TextView)findViewById(R.id.user_summary_bio); } public void setUser(User user){ Picasso.with(getContext()) .load(user.getProfileImageUrl()) .into(profileImage); name.setText(user.getName()); title.setText(user.getJobTitle()); bio.setText(user.getBio()); }
private void init(Context context){
setOrientation(VERTICAL);
LayoutInflater.from(context).inflate( R.layout.view_user_summary, this); profileImage = (ImageView)findViewById(R.id.user_summary_image); name = (TextView)findViewById(R.id.user_summary_name); title = (TextView)findViewById(R.id.user_summary_title); bio = (TextView)findViewById(R.id.user_summary_bio); } public void setUser(User user){ Picasso.with(getContext()) .load(user.getProfileImageUrl()) .into(profileImage); name.setText(user.getName()); title.setText(user.getJobTitle()); bio.setText(user.getBio()); }
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:id="@+id/user_summary_image" android:layout_width="match_parent" android:layout_height="256dp" tools:src="@drawable/profile_sample" android:scaleType="centerCrop" /> <TextView tools:text="Luke Sleeman" android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/user_summary_name" …
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:id="@+id/user_summary_image" android:layout_width="match_parent" android:layout_height="256dp" tools:src="@drawable/profile_sample" android:scaleType="centerCrop" /> <TextView tools:text="Luke Sleeman" android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/user_summary_name" …
<?xml version="1.0" encoding="utf-8"?> <merge xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" tools:parentTag="LinearLayout" tools:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:id="@+id/user_summary_image" android:layout_width="match_parent" android:layout_height="256dp" tools:src="@drawable/profile_sample" android:scaleType="centerCrop" /> <TextView tools:text="Luke Sleeman" android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/user_summary_name" …
<?xml version="1.0" encoding="utf-8"?> <merge xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" tools:parentTag="LinearLayout" tools:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:id="@+id/user_summary_image" android:layout_width="match_parent" android:layout_height="256dp" tools:src="@drawable/profile_sample" android:scaleType="centerCrop" /> <TextView tools:text="Luke Sleeman" android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/user_summary_name" …
… <com.example.UserSummaryView android:id="@+id/main_summary_view" android:layout_width="match_parent" android:layout_height="match_parent" /> … … @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); UserSummaryView summaryView = (UserSummaryView)findViewById(R.id.main_summary_view); summaryView.setUser(testUser); } …
… <com.example.UserSummaryView android:id="@+id/main_summary_view" android:layout_width="match_parent" android:layout_height="match_parent" /> … … @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); UserSummaryView summaryView = (UserSummaryView)findViewById(R.id.main_summary_view); summaryView.setUser(testUser); } …
… <com.example.UserSummaryView android:id="@+id/main_summary_view" android:layout_width="match_parent" android:layout_height="match_parent" /> … … @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); UserSummaryView summaryView = (UserSummaryView)findViewById(R.id.main_summary_view); summaryView.setUser(testUser); } …
public abstract class AbstractDrawerActivity extends AppCompatActivity { protected void startActivityAndCloseDrawer(Class<? extends Activity> toStart) { // Same activity, just return if (getActivity().getClass().equals(toStart)) return; final Intent launchIntent = new Intent(getActivity(),toStart) .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); drawerLayout.closeDrawer(GravityCompat.START); drawerLayout.postDelayed(new Runnable() { @Override public void run() { startActivity(launchIntent); getActivity().overridePendingTransition(R.anim.activity_in, R.anim.activity_out); } }, CLOSE_DELAY); }
public abstract class AbstractDrawerActivity extends AppCompatActivity { protected void startActivityAndCloseDrawer(Class<? extends Activity> toStart) { // Same activity, just return if (getActivity().getClass().equals(toStart)) return; final Intent launchIntent = new Intent(getActivity(),toStart) .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); drawerLayout.closeDrawer(GravityCompat.START); drawerLayout.postDelayed(new Runnable() { @Override public void run() { startActivity(launchIntent); getActivity().overridePendingTransition(R.anim.activity_in, R.anim.activity_out); } }, CLOSE_DELAY); }
public abstract class AbstractDrawerActivity extends AppCompatActivity { protected void startActivityAndCloseDrawer(Class<? extends Activity> toStart) { // Same activity, just return if (getActivity().getClass().equals(toStart)) return; final Intent launchIntent = new Intent(getActivity(),toStart) .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); drawerLayout.closeDrawer(GravityCompat.START); drawerLayout.postDelayed(new Runnable() { @Override public void run() { startActivity(launchIntent); getActivity().overridePendingTransition(R.anim.activity_in, R.anim.activity_out); } }, CLOSE_DELAY); }
public abstract class AbstractDrawerActivity extends AppCompatActivity { protected void startActivityAndCloseDrawer(Class<? extends Activity> toStart) { // Same activity, just return if (getActivity().getClass().equals(toStart)) return; final Intent launchIntent = new Intent(getActivity(),toStart) .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); drawerLayout.closeDrawer(GravityCompat.START); drawerLayout.postDelayed(new Runnable() { @Override public void run() { startActivity(launchIntent); getActivity().overridePendingTransition(R.anim.activity_in, R.anim.activity_out); } }, CLOSE_DELAY); }
public abstract class AbstractDrawerActivity extends AppCompatActivity { protected void startActivityAndCloseDrawer(Class<? extends Activity> toStart) { // Same activity, just return if (getActivity().getClass().equals(toStart)) return; final Intent launchIntent = new Intent(getActivity(),toStart) .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); drawerLayout.closeDrawer(GravityCompat.START); drawerLayout.postDelayed(new Runnable() { @Override public void run() { startActivity(launchIntent); getActivity().overridePendingTransition(R.anim.activity_in, R.anim.activity_out); } }, CLOSE_DELAY); }
public abstract class AbstractDrawerActivity extends AppCompatActivity { protected void startActivityAndCloseDrawer(Class<? extends Activity> toStart) { // Same activity, just return if (getActivity().getClass().equals(toStart)) return; final Intent launchIntent = new Intent(getActivity(),toStart) .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); drawerLayout.closeDrawer(GravityCompat.START); drawerLayout.postDelayed(new Runnable() { @Override public void run() { startActivity(launchIntent); getActivity().overridePendingTransition(R.anim.activity_in, R.anim.activity_out); } }, CLOSE_DELAY); }
<!—- Animate in —-> <set xmlns:android="http://schemas.android.com/apk/res/android"> <alpha android:duration="@integer/activity_animation_duration" android:fromAlpha="0.0" android:toAlpha="1.0" android:interpolator="@android:anim/decelerate_interpolator"/> <translate android:duration="@integer/activity_animation_duration" android:fromYDelta="15%" android:toYDelta="0%" android:interpolator="@android:anim/decelerate_interpolator"/> </set> <!—- Animate out —-> <set xmlns:android="http://schemas.android.com/apk/res/android" > <alpha android:duration="@integer/activity_animation_duration" android:fromAlpha="1.0" android:toAlpha="0.0" android:interpolator="@android:anim/accelerate_interpolator"/> <translate android:duration="@integer/activity_animation_duration" android:fromYDelta="0%" android:toYDelta="-15%" android:interpolator="@android:anim/accelerate_interpolator"/> </set>
public class ViewListAdapter extends PagerAdapter { private ViewGroup [] views; public CustomPagerAdapter(ViewGroup... views) { this.views = views; } @Override public Object instantiateItem(ViewGroup collection, int position) { ViewGroup layout = views[position]; collection.addView(layout); return layout; } @Override public void destroyItem(ViewGroup collection, int position, Object view) { collection.removeView((View) view); } @Override public int getCount() { return views.length; } @Override public boolean isViewFromObject(View view, Object object) { return view == object; } }
public class ViewListAdapter extends PagerAdapter { private ViewGroup [] views; public CustomPagerAdapter(ViewGroup... views) { this.views = views; } @Override public Object instantiateItem(ViewGroup collection, int position) { ViewGroup layout = views[position]; collection.addView(layout); return layout; } @Override public void destroyItem(ViewGroup collection, int position, Object view) { collection.removeView((View) view); } @Override public int getCount() { return views.length; } @Override public boolean isViewFromObject(View view, Object object) { return view == object; } }
public class ViewListAdapter extends PagerAdapter { private ViewGroup [] views; public CustomPagerAdapter(ViewGroup... views) { this.views = views; } @Override public Object instantiateItem(ViewGroup collection, int position) { ViewGroup layout = views[position]; collection.addView(layout); return layout; } @Override public void destroyItem(ViewGroup collection, int position, Object view) { collection.removeView((View) view); } @Override public int getCount() { return views.length; } @Override public boolean isViewFromObject(View view, Object object) { return view == object; } }
(Well, perhaps not)
Cancel short tasks
and-mortar/
Screen Screen Screen
Activity
RecipeSearch ViewRecipe
flow.set(new ViewRecipeKey(234));
RecipeSearch ViewRecipe
flow.goBack();
Custom View Presenter
// Lifecycle methods void onEnterScope(MortarScope scope) void onLoad(Bundle savedInstanceState) void onSave(Bundle outState) void onExitScope() “Mortar provides a simplified, composable overlay for the Android lifecycle”
Mitigate the risks if you do use them
https://youtu.be/k3IT-IJ0J98
fragment when a ViewGroup or <include> will do”
the knowledge of external lifecycle
DO NOT ‘go fishing’ for things when you get attached to an activity - go looking for other fragments, try and pull in various deps, etc
Beware of dependencies! You can use onInflate() and fragment args to get data in
Activity List Fragment Detail Fragment
? ? ???
static final int INITIALIZING = 0; // Not yet created. static final int CREATED = 1; // Created. static final int ACTIVITY_CREATED = 2; // The activity has finished its creation. static final int STOPPED = 3; // Fully created, not started. static final int STARTED = 4; // Created and started, not resumed. static final int RESUMED = 5; // Created started and resumed.
listeners, etc
are dangerous
Async fragment transactions!
problems - where to put them in your architecture
take on responsibilities outside of its self”
views at once. Depends on how many pages your caching
new fragment using getItem or may re-use an already constructed fragment by looking it up by ID
which deal with state in a different manner
EG, you may have a Fragment which takes up the screen, which contains a view pager inside it, which of course has other fragments
Or fragments based on position
you can’t reliably modify their contents, get fragment at index, etc.
be called ?! Often depends on position index, and what page your are selecting.
to update some stuff (activity action bar colour). It works fine when I select next page, or two pages
java.lang.IllegalStateException: Fragment TimelineListFragment_{b20af32 id=0x7f0c00bb android:switcher: 2131493051:1949793103} not attached to Activity When I select something 5 pages across
S - Single Responsibility Principal O L I D
and it does it all - e.g., the map fragment
presentation
mortar/
fragments.html
advocating_against_android_fragments/