Some Launchers will launch an app at it’s LAUNCH activity on top of the last point the user was at. Imagine: 1. User opens app. 2. The Launcher Activity is designated at Activity A, so that’s what opens. 3. User navigates several Activities deep and ends up at Activity D (A > B > C […]
The title isn’t exactly accurate – you can’t “restart” an app per se – but you can get pretty close:
1 2 3 4 |
// presumably in an Activity; otherwise a Context instance will be needed for several methods Intent intent = getPackageManager().getLaunchIntentForPackage(getPackageName()); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); startActivity(intent); |
This starts the Activity specified in your manifest as the “LAUNCHER”, in a new task stack (history), and clear the existing stack.
In xml, you can use ?attr/actionBarSize, but if you need access to that value in Java…
1 2 3 4 5 6 |
public int getActionBarSize() { TypedArray styledAttributes = getTheme().obtainStyledAttributes(new int[] { android.R.attr.actionBarSize }); int actionBarSize = (int) styledAttributes.getDimension(0, 0); styledAttributes.recycle(); return actionBarSize; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 |
package com.qozix.view; import android.view.View; import android.view.ViewGroup; import android.view.ViewParent; import java.util.ArrayList; import java.util.Collection; /** * Collection of utility functions for finding Views in a hierarchy. */ public class Finder { /** * Interface with method to determine if a particular View meets arbitrary criteria for inclusion in a * ViewFinder method's result set. */ public interface Qualifier { /** * Qualify boolean. * * @param view the view * @return the boolean */ boolean qualify(View view); } /** * Helper method to pre-cast Views to the type of the declared instance. * * @param <T> The Class reference (View subclass) the View should be cast to. * @param viewGroup The ViewGroup instance to search for the View specified by #id. * @param id The id of the View to be located within this Activity's view tree. * @return The View with the matching id. */ @SuppressWarnings("unchecked") public <T extends View> T findAndCast(ViewGroup viewGroup, int id) { return (T) viewGroup.findViewById(id); } /** * Utility * * @param qualifier * @param view * @return */ private static boolean qualifies(Qualifier qualifier, View view) { return qualifier == null || qualifier.qualify(view); } /** * Utility * * @param view the view * @return view group */ public static ViewGroup getContainingViewGroup(View view) { ViewParent viewParent = view.getParent(); if (viewParent instanceof ViewGroup) { return (ViewGroup) viewParent; } return null; } /** * Find first view. * * @param viewGroup the view group * @param qualifier the qualifier * @return the view */ public static View findFirst(ViewGroup viewGroup, Qualifier qualifier) { for (int i = 0; i < viewGroup.getChildCount(); i++) { View child = viewGroup.getChildAt(i); if (qualifies(qualifier, child)) { return child; } if (child instanceof ViewGroup) { View match = findFirst((ViewGroup) child, qualifier); if (match != null) { return match; } } } return null; } /** * Find all collection. * * @param viewGroup the view group * @return the collection */ public static Collection<View> findAll(ViewGroup viewGroup) { return findAll(viewGroup, null); } /** * Find all collection. * * @param viewGroup the view group * @param qualifier the qualifier * @return the collection */ public static Collection<View> findAll(ViewGroup viewGroup, Qualifier qualifier) { return findAll(viewGroup, qualifier, null); } /** * Find all collection. * * @param viewGroup the view group * @param qualifier the qualifier * @param out the out * @return the collection */ public static Collection<View> findAll(ViewGroup viewGroup, Qualifier qualifier, Collection<View> out) { if (out == null) { out = new ArrayList<>(); } for (int i = 0; i < viewGroup.getChildCount(); i++) { View child = viewGroup.getChildAt(i); if (qualifies(qualifier, child)) { out.add(child); } if (child instanceof ViewGroup) { findAll((ViewGroup) child, qualifier, out); } } return out; } /** * Get first child view. * * @param viewGroup the view group * @return the view */ public static View getFirstChild(ViewGroup viewGroup) { return viewGroup.getChildAt(0); } /** * Get first child view. * * @param viewGroup the view group * @param qualifier the qualifier * @return the view */ public static View getFirstChild(ViewGroup viewGroup, Qualifier qualifier) { for (int i = 0; i < viewGroup.getChildCount(); i++) { View child = viewGroup.getChildAt(i); if (qualifies(qualifier, child)) { return child; } } return null; } /** * Get last child view. * * @param viewGroup the view group * @return the view */ public static View getLastChild(ViewGroup viewGroup) { return viewGroup.getChildAt(viewGroup.getChildCount() - 1); } /** * Get last child view. * * @param viewGroup the view group * @param qualifier the qualifier * @return the view */ public static View getLastChild(ViewGroup viewGroup, Qualifier qualifier) { for (int i = viewGroup.getChildCount() - 1; i >= 0; i--) { View child = viewGroup.getChildAt(i); if (qualifies(qualifier, child)) { return child; } } return null; } /** * Get children collection. * * @param viewGroup the view group * @return the collection */ public static Collection<View> getChildren(ViewGroup viewGroup) { return getChildren(viewGroup, null); } /** * @param viewGroup * @param qualifier * @return */ public static Collection<View> getChildren(ViewGroup viewGroup, Qualifier qualifier) { return getChildren(viewGroup, qualifier, new ArrayList<>()); } /** * Get children collection. * * @param viewGroup the view group * @param qualifier the qualifier * @param out * @return the collection */ public static Collection<View> getChildren(ViewGroup viewGroup, Qualifier qualifier, Collection<View> out) { for (int i = 0; i < viewGroup.getChildCount(); i++) { View child = viewGroup.getChildAt(i); if (qualifies(qualifier, child)) { out.add(child); } } return out; } /** * Gets ancestors. * * @param view the view * @return the ancestors */ public static Collection<ViewGroup> getAncestors(View view) { return getAncestors(view, null); } /** * Gets ancestors. * * @param view the view * @param qualifier the qualifier * @return the ancestors */ public static Collection<ViewGroup> getAncestors(View view, Qualifier qualifier) { return getAncestors(view, qualifier, new ArrayList<>()); } /** * Gets ancestors. * * @param view the view * @param qualifier the qualifier * @param out * @return the ancestors */ public static Collection<ViewGroup> getAncestors(View view, Qualifier qualifier, Collection<ViewGroup> out) { ViewGroup parent = getContainingViewGroup(view); while (parent != null) { if (qualifies(qualifier, parent)) { out.add(parent); } parent = getContainingViewGroup(parent); } return out; } /** * Get siblings collection. * * @param view the view * @return the collection */ public static Collection<View> getSiblings(View view) { return getSiblings(view, null); } /** * Get siblings collection. * * @param view the view * @param qualifier the qualifier * @return the collection */ public static Collection<View> getSiblings(View view, Qualifier qualifier) { ViewGroup parent = getContainingViewGroup(view); if (parent == null) { return null; } Collection<View> siblings = new ArrayList<>(); for (int i = 0; i < parent.getChildCount(); i++) { View child = parent.getChildAt(i); if (child != view && qualifies(qualifier, child)) { siblings.add(child); } } return siblings; } /** * Get previous sibling view. * * @param view the view * @return the view */ public static View getPreviousSibling(View view) { return getPreviousSibling(view, null); } /** * Get previous sibling view. * * @param view the view * @param qualifier the qualifier * @return the view */ public static View getPreviousSibling(View view, Qualifier qualifier) { ViewGroup parent = getContainingViewGroup(view); if (parent == null) { return null; } for (int i = 0; i < parent.getChildCount(); i++) { View child = parent.getChildAt(i); if (child == view) { for (; i > 0; ) { View sibling = parent.getChildAt(--i); if (qualifies(qualifier, sibling)) { return sibling; } } } } return null; } /** * Get next sibling view. * * @param view the view * @return the view */ public static View getNextSibling(View view) { return getNextSibling(view, null); } /** * Get next sibling view. * * @param view the view * @param qualifier the qualifier * @return the view */ public static View getNextSibling(View view, Qualifier qualifier) { ViewGroup parent = getContainingViewGroup(view); if (parent == null) { return null; } for (int i = 0; i < parent.getChildCount(); i++) { View child = parent.getChildAt(i); if (child == view) { while (i < parent.getChildCount()) { View sibling = parent.getChildAt(++i); if (qualifies(qualifier, sibling)) { return sibling; } } } } return null; } /** * Get previous siblings collection. * * @param view the view * @return the collection */ public static Collection<View> getPreviousSiblings(View view) { return getPreviousSiblings(view, null); } /** * Get previous siblings collection. * * @param view the view * @param qualifier the qualifier * @return the collection */ public static Collection<View> getPreviousSiblings(View view, Qualifier qualifier) { ViewGroup parent = getContainingViewGroup(view); if (parent == null) { return null; } Collection<View> siblings = new ArrayList<>(); for (int i = 0; i < parent.getChildCount(); i++) { View child = parent.getChildAt(i); if (child == view) { break; } if (qualifies(qualifier, child)) { siblings.add(child); } } return siblings; } /** * Get next siblings collection. * * @param view the view * @return the collection */ public static Collection<View> getNextSiblings(View view) { return getNextSiblings(view, null); } /** * Get next siblings collection. * * @param view the view * @param qualifier the qualifier * @return the collection */ public static Collection<View> getNextSiblings(View view, Qualifier qualifier) { ViewGroup parent = getContainingViewGroup(view); if (parent == null) { return null; } Collection<View> siblings = new ArrayList<>(); boolean hasFoundReference = false; for (int i = 0; i < parent.getChildCount(); i++) { View child = parent.getChildAt(i); if (hasFoundReference && qualifies(qualifier, view)) { siblings.add(child); } if (child == view) { hasFoundReference = true; } } return siblings; } /** * Finds the first child in #rootView that is an instance of #clazz * * Use this instead of #findAll with an instanceof Qualifier in order to get typed returns, * e.g., Collection<TextView> textViews = ViewFinder.findAllByClass(someViewGroup, TextView.class); * * @param <T> The type of View subclass to search for. * @param viewGroup The View whose hierarchy should be examined for instances of #clazz. * @param clazz The Class to search for within #rootView. * @return The first child in #rootView this is an instance of #clazz. */ public static <T extends View> T findByClass(ViewGroup viewGroup, Class<T> clazz) { for (int i = 0; i < viewGroup.getChildCount(); i++) { View child = viewGroup.getChildAt(i); if (clazz.isInstance(child)) { return clazz.cast(child); } if (child instanceof ViewGroup) { T match = findByClass((ViewGroup) child, clazz); if (match != null) { return match; } } } return null; } /** * Returns a Collection of View subclasses instances of type T found within #rootView. * * Use this instead of #findAll with an instanceof Qualifier in order to get typed returns, * e.g., Collection<TextView> textViews = ViewFinder.findAllByClass(someViewGroup, TextView.class, null); * * @param <T> The type of View subclass to search for. * @param viewGroup The View whose hierarchy should be examined for instances of #clazz. * @param clazz The Class to search for within #rootView. * @param out A Collection of View subclasses of type T that will be populated with matches found in #rootView. * @return A Collection of View subclasses instances of type T found within #rootView. */ public static <T extends View> Collection<T> findAllByClass(ViewGroup viewGroup, Class<T> clazz, Collection<T> out) { for (int i = 0; i < viewGroup.getChildCount(); i++) { View child = viewGroup.getChildAt(i); if (clazz.isInstance(child)) { out.add(clazz.cast(child)); } if (child instanceof ViewGroup) { findAllByClass((ViewGroup) child, clazz, out); } } return out; } /** * Returns a Collection of View subclasses instances of type T found within #rootView. * * @param <T> The type of View subclass to search for. * @param viewGroup The View whose hierarchy should be examined for instances of #clazz. * @param clazz The Class to search for within #rootView. * @return A Collection of View subclasses instances of type T found within #rootView. */ public static <T extends View> Collection<T> findAllByClass(ViewGroup viewGroup, Class<T> clazz) { return findAllByClass(viewGroup, clazz, new ArrayList<>()); } } |
Add (or amend) your values/attrs.xml to include:
1 2 3 4 5 6 7 |
<resources> <declare-styleable name="SeparatorLinearLayout"> <attr name="separator" format="reference" /> <attr name="separatorHeight" format="integer" /> <attr name="separatorWidth" format="integer" /> </declare-styleable> </resources> |
Some layout:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<?xml version="1.0" encoding="utf-8"?> <com.qozix.widgets.SeparatorLinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" app:separator="?android:attr/listDivider"><!-- any drawable or color --> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="12sp" android:text="This is a row" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="12sp" android:text="This is a row" /> </com.qozix.widgets.SeparatorLinearLayout> |
And the class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
package com.qozix.widgets; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.View; import android.widget.LinearLayout; import com.qozix.widgets.R; /** * Created by michaeldunn on 8/15/16. */ public class SeparatorLinearLayout extends LinearLayout { private Drawable mSeparator; private Rect mSeparatorBounds = new Rect(); private int mSeparatorHeight; private int mSeparatorWidth; public SeparatorLinearLayout(Context context) { this(context, null); } public SeparatorLinearLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public SeparatorLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); setWillNotDraw(false); TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.SeparatorLinearLayout, 0, 0); try { Drawable drawable = typedArray.getDrawable(R.styleable.SeparatorLinearLayout_separator); if (drawable != null) { setSeparator(drawable); } int drawableWidth = typedArray.getInt(R.styleable.SeparatorLinearLayout_separatorWidth, 0); if (drawableWidth != 0) { setSeparatorWidth(drawableWidth); } int drawableHeight = typedArray.getInt(R.styleable.SeparatorLinearLayout_separatorHeight, 0); if (drawableHeight != 0) { setSeparatorHeight(drawableHeight); } } finally { typedArray.recycle(); } } public void setSeparator(Drawable drawable) { mSeparator = drawable; if (mSeparator != null) { mSeparatorHeight = mSeparator.getIntrinsicHeight(); mSeparatorWidth = mSeparator.getIntrinsicWidth(); } } public void setSeparatorHeight(int separatorHeight) { mSeparatorHeight = separatorHeight; } public void setSeparatorWidth(int separatorWidth) { mSeparatorWidth = separatorWidth; } private void drawVerticalSeparators(Canvas canvas) { mSeparatorBounds.left = 0; mSeparatorBounds.right = canvas.getWidth(); for (int i = 0; i < getChildCount() - 1; i++) { View child = getChildAt(i); mSeparatorBounds.top = child.getBottom() - 1; mSeparatorBounds.bottom = mSeparatorBounds.top + mSeparatorHeight; drawDrawableToCanvas(canvas); } } private void drawHorizontalSeparators(Canvas canvas) { mSeparatorBounds.top = 0; mSeparatorBounds.bottom = canvas.getHeight(); for (int i = 0; i < getChildCount() - 1; i++) { View child = getChildAt(i); mSeparatorBounds.left = child.getRight() - 1; mSeparatorBounds.right = mSeparatorBounds.left + mSeparatorWidth; drawDrawableToCanvas(canvas); } } private void drawDrawableToCanvas(Canvas canvas) { mSeparator.setBounds(mSeparatorBounds); mSeparator.draw(canvas); } private void drawSeparators(Canvas canvas) { switch (getOrientation()) { case VERTICAL: drawVerticalSeparators(canvas); break; case HORIZONTAL: drawHorizontalSeparators(canvas); break; } } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (mSeparator != null) { drawSeparators(canvas); } } } |
The behavior here isn’t quite what I expected, or gather from the documentation. If you have <activity android:name=”.SomeActivity” android:launchMode=”singleTop” /> Then you start SomeActivity multiple times:
1 2 3 4 5 6 7 8 |
private void doIt(){ startSomeActivity(); startSomeActivity(); } private void startSomeActivity(){ Intent intent = new Intent(this, SomeActivity.class); startActivity(intent); } |
You’ll get only one instance of SomeActivity at the top of the stack (as the singleTop launch mode promises), but you’ll notice that if you finish() or hit […]
Possibly useful in precomputing layout requirements…
1 2 3 4 5 6 7 8 9 |
public static int getDefaultTextSize(Context context) { TypedValue typedValue = new TypedValue(); context.getTheme().resolveAttribute(android.R.attr.textAppearance, typedValue, true); int[] textSizeAttr = new int[] { android.R.attr.textSize }; TypedArray typedArray = context.obtainStyledAttributes(typedValue.data, textSizeAttr); int textSize = typedArray.getDimensionPixelSize(0, -1); typedArray.recycle(); return textSize; } |
When extending a typed class without specifying the type, just pass the type to the subclass’s erasure and specify that type in the reference to the superclass. E.g.,
1 2 3 |
public class MyClass<E extends List> {} public class MySubClass<E extends List> extends MyClass<E> {} // E is already declared by the time we get to MyClass, so just reference it - instead of defining it - as a type param for the superclass |
As an example, consider the AOSP class RecyclerView.Adapter, which is declared as:
1 |
public static abstract class Adapter<VH extends ViewHolder> |
A normal concrete implementation is fairly straightforward:
1 |
public class MyAdapter extends Adapter<MyHolder> |
But what if you want […]
Probably unexpected behavior:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public class Main { public static void main(String[] args) { mew SomeSub(); } public abstract static class SomeSuper { public SomeSuper(){ doInit(); } public abstract void doInit(); } public static class SomeSub extends SomeSuper { public int someIntMember = 3; public void doInit(){ System.out.println(someIntMember); // prints 0, not 3 } } } |
There’s not a direct way to work around this – often initialization or setup methods are used, sometimes with a Factory instance generator.
Butterknife, like most things by Square and/or Jake Wharton, is a very handy, very popular library. One thing that was really nice was how small and simple it was. The latest version, however, is a lot less simple, and requires multiple gradle dependencies and the apt plugin. Not a huge lift, but not exactly as […]