The Android APIs That Can Improve Your Working Efficiency Exponentially

Published Jun 26, 2017Last updated Dec 11, 2017
The Android APIs That Can Improve Your Working Efficiency Exponentially

“All roads lead to Rome.”

At work, there's almost always more than one way to implement a solution. These implementations not only influence the quality of our code, but they also influence our overall efficiency as well. For instance, in Android, there are always a few APIs that can really cut down on our workload — it’s just that most people don’t know about them. So based on my experience, I want to compile some APIs that I use often, and collect them somewhere for future reference.

getResources().getIdentifier(String name, String defType, String defPackage)

Get resource ID using resource name. Normally, we will simply get resources based on resource ID in the code as in the following example:

getResources().getDrawable(R.drawable.ic_launcher);

But sometimes, the resource is not fixed. Depending on the situation, resources need to be dynamically selected from a list of resources of the same type. For example, what happens when you need to decide what resources are needed based on the dynamic data devices as informed by the server? Set up a hard-coded mapping array? Too much work! Try the following instead.

A complete resource name comes in the form package:type/entry, with three parameters corresponding to the method: resource name, resource type, and package name. A couple examples follow:

imageView.setImageResource(getResources().getIdentifier("ic_launcher", "drawable", getPackageName()));
 
radioGroup.check(getResources().getIdentifier("rb_indicator_" + position, "id", getPackageName()));

In actual use, the first parameter name (or resource name), needs to be set dynamically. This requires the different resources to use the same name format. What’s worth noting is that using ID to get resources is more efficient than using the name. So, if you don’t have a special reason for it, I don't recommend getting resources this way.

TextUtils.isEmpty(CharSequence str)

This is an extremely frequently used method of determining whether a string is empty. The function returns a boolean that is implmented internally using the following expression: str == null || str.length() == 0. The ever-popular if statement for determining whether the string is empty has been packaged by the system.

Html.fromHtml()

Reads HTML rich text content and returns a string with formatting to be viewed in viewers like TextView. This solves the display problem that some rich texts have when it includes formats such as hyperlinks and pictures embedded in text.

DateUtils.formatDateTime()

Using this date utility from the Android SDK, a long type millisecond time data can be formatted as strings with a specific display format. Usually we use SimpleDateFormat from the Java SDK to format date data, for example new SimpleDateFormat("yyyy-MM-dd HH:mm").format(). DateUtils has packaged this process. The result of will depend on the local language of the current device. Here we provide an example of a few frequently used format (Chinese environment):

  • FORMAT_SHOW_DATE:3月3日(March 3rd)
  • FORMAT_SHOW_TIME:10:37
  • FORMAT_SHOW_WEEKDAY:星期五 (Friday)
  • FORMAT_SHOW_YEAR:2017年3月3日 (March 3rd, 2017)
  • FORMAT_NUMERIC_DATE:3/3
  • FORMAT_NO_MONTH_DAY:三月 (March)

Formatter.formatFileSize`(Context context, long sizeBytes)

Formats the size of the document from byte value to value corresponding to units of B, KB, M. The context parameter is used to determine whether the order of the string in the returned results is written from right-to-left or left-to-right. This utility allows us to avoid converting and calculating on our own and is very convenient, especially when used in situations like downloading internal documents.

TypedValue.applyDimension(int unit, float value, DisplayMetrics metrics)

Takes size dimensions in a specific unit and converts it to the corresponding pixel value based on the current screen settings. TypedValue provides a few of the most frequently used units in the first parameter, such as:

  • COMPLEX_UNIT_PX
  • COMPLEX_UNIT_DIP
  • COMPLEX_UNIT_PT
  • COMPLEX_UNIT_SP

The source code is as follows:

public static float applyDimension(int unit, float value, DisplayMetrics metrics){
    switch (unit) {
    case COMPLEX_UNIT_PX:
        return value;
    case COMPLEX_UNIT_DIP:
        return value * metrics.density;
    case COMPLEX_UNIT_SP:
        return value * metrics.scaledDensity;
    case COMPLEX_UNIT_PT:
        return value * metrics.xdpi * (1.0f/72);
    case COMPLEX_UNIT_IN:
        return value * metrics.xdpi;
    case COMPLEX_UNIT_MM:
        return value * metrics.xdpi * (1.0f/25.4f);
    }
    return 0;
}

In practice, pixel values are always integers int, but the function returns a float. If we convert this anyways, the decimals will be dropped. So if we don't use this function, usually we’ll go about it like so:

public static int convertDipToPx(Context context, int dip) {
    float scale = context.getResources().getDisplayMetrics().density;
    // 0.5f used to round up
    return (int) (dip * scale + 0.5f * (dip >= 0 ? 1 : -1));
}

Space

Here’s the official description:

Space is a lightweight View subclass that may be used to create gaps between components in general purpose layouts.

Since the onDraw() function does not draw, the android:background property doesn’t work. Usually we’ll use View to create gaps between components in a layout. If we ignore the background color, Space is actually more efficient. Note that this is a widget introduced in API 14. For backward compatibility, the support v4 package needs to be used.

view.performClick()

Automatically evokes click event with View. Usually, events can only be evoked when the user clicks on controls like buttons. This function can be used in special circumstances to simulate a user click. Similar functions include performLongClick().

Log.getStackTraceString(Throwable tr)

The Log class provides a public static function. What’s different from functions, such as log.i(), that print a log to the logcat control panel is that the function obtains error messages from objects thrown by Throwable and returns it as a string. When you need to preserve error message data, such as when saving to a local memory card or upload it to a server, this method makes it very convenient.

We know that with contents in the text widget TextView, we can add hyperlinked click events that follow fixed templates, such as web and phone, through the android:autoLink property. But of course there's a limited number of system templates. Using Linkify.addLinks() can allow you to add some app-specific customized templates, such as the “@XXX” hyperlink used in Weibo. These can all be processed through customized regular expressions.

getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE)

Sets up secure windows and disallows screen capture from the system. This can disable screen capture in certain apps and prevent information from being leaked that way. (Usually mobile phone system screen captures are performed by simultaneously pressing the power and volume buttons.)

For example, the “Pay Store” page that includes a QR code for payment in the Alipay app. (As a side note, this is not how the payment interface in Wechat work. Wechat refreshes the payment QR code in real-time in the onResume() lifecycle, very different from Alipay in terms of how they implement security.)

Intercept the Back button, so the App runs in the background rather than terminate

  @Override
  public void onBackPressed() {
    Intent launcherIntent = new Intent(Intent.ACTION_MAIN);
    launcherIntent.addCategory(Intent.CATEGORY_HOME);
    startActivity(launcherIntent);
  }

Use the back button to return to the desktop and let the app run in the background rather than closing it, just like when the Home button is pressed.
This is a very cool technique, usually used to prevent users from closing the app when they accidentally press the back button. How it works is that we’ll monitor the back button in the app frontpage’s Activity and use Toast or even Dialog notification to give users a chance to confirm their operations. But this doesn’t stop users from shutting down the app by repeatedly using the back button.

With that said, if we use this method to intercept the user's last activity in the app (usually on the frontpage), we’ll simultaneously give users what they want (return to desktop), and get what we want (keep our app running in the background).This indirectly increases the app survival time. A true switcheroo. And from what I’ve seen, apps like Wechat, Alipay, and Weibo all do this. You can check it out yourself.

ThumbnailUtils

A tool class for generation thumbnail that generates thumbnails based on the local video source or bitmaps. The common public static methods are:

  • createVideoThumbnail(String filePath, int kind)
  • extractThumbnail(Bitmap source, int width, int height)

bitmap.extractAlpha()

Extracting a new bitmap from the alpha channel of the source bitmap (what a mouthful!). Generally speaking, the app icon backgrounds are made with pure color transparent pixels. This method can color the non-transparent areas of the image and can come in handy in various scenarios such as a pressed button or the shadows in view. For example:

private static Bitmap getDropShadow(ImageView iv, Bitmap src, float radius, int color) {
 
    final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    paint.setColor(color);
 
    final int width = src.getWidth(), height = src.getHeight();
    final Bitmap dest = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
    final Canvas canvas = new Canvas(dest);
    final Bitmap alpha = src.extractAlpha();
    canvas.drawBitmap(alpha, 0, 0, paint);
 
    final BlurMaskFilter filter = new BlurMaskFilter(radius, BlurMaskFilter.Blur.OUTER);
    paint.setMaskFilter(filter);
    canvas.drawBitmap(alpha, 0, 0, paint);
    iv.setImageBitmap(dest);
 
    return dest;
}

ArgbEvaluator

The system provides a TypeEvaluator, and we just have to provide two starting color values and a score for the system to calculate a new middle color according to a special algorithm. This class allows us to do at least two more things.
First, this can be used in property animations. Since this works as a TypeEvaluator interface, it can be used as a customized property animation evaluator to change the display status of View. For example:

int colorStart = ContextCompat.getColor(this, R.color.black);
int colorEnd = ContextCompat.getColor(this, R.color.green);
ValueAnimator valueAnimator = ValueAnimator
    .ofObject(new ArgbEvaluator(), colorStart, colorEnd)
    .setDuration(3000);
valueAnimator.addUpdateListener(new AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        textView.setTextColor((Integer) animation.getAnimatedValue());
    }
});
valueAnimator.start();

Second, the color evaluation algorithm provided by the class can be used with the scroll offset of ViewPager. This is often seen in guide pages that were implemented using ViewPager, where the background color dynamically changes with the scroll distance. Or when using a tabbed menu page implemented using ViewPager, the text color of the tab content dynamically changes color with the scroll distance (see the Weibo app for Android). Both of these implementations make ViewPager page transitions natural for an excellent user experience. For example:

viewPager.addOnPageChangeListener(new OnPageChangeListener() {
    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        new ArgbEvaluator().evaluate(positionOffset, startColor, endColor);
    }
 
    @Override
    public void onPageSelected(int position) {
 
    }
 
    @Override
    public void onPageScrollStateChanged(int state) {
 
    }
});

For the calculation of the color change, Google Sample offers a different method (see SlidingTabStrip.java). Here’s the gist of it:

/**
* Blend {@code color1} and {@code color2} using the given ratio.
*
* @param ratio of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend,
*              0.0 will return {@code color2}.
*/
private static int blendColors(int color1, int color2, float ratio) {
    final float inverseRation = 1f - ratio;
    float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation);
    float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation);
    float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation);
    return Color.rgb((int) r, (int) g, (int) b);
}

android:weightSum

Used to set the sum of the children weights in LinearLayout. For children in LinearLayout, we often use android:layout_weight to proportionately distribute the space for container-layout, but sometimes there will still be remaining space. In the past, some people would insert a space to fill out the end. If you know about this property, though, you can save yourself a few lines of code.

android:descendantFocusability

Used in ViewGroup as a solution to the issue of changing focus between the parent view and the children view. The most common scenario is the list item, where there are some widgets with click effects, such as buttons and checkboxes. I'm sure you are familiar with those. There are three values, which I won’t elaborate on here:

  • afterDescendants
  • beforeDescendants
  • blocksDescendants

android:duplicateParentState

Used to decide whether to give control of the drawable state of the View over to the parent ViewGroup. This is a boolean value. For example, for a button in an item layout which needs to present a click effect when the item layout is clicked, this property can be used. However, from a design point of view, this type of scenario rarely happens. Just know that this exists, but I don't recommend this sort of interaction design.

android:fillViewport

A property of ScrollView used to configure whether the content should fill the screen. This is used when there is not enough content to fill the entire screen. I recommend a specific technique for this situation — you can read about it in my earlier post: Two Practical Layout Techniques for Android Development (original post in Chinese).

android:adjustViewBounds

When using ImageView, you may use the android:scaleType property for deciding how to set the size of an image. What you may not know is that the android:adjustViewBounds property can achieve the same effect. With that said, note that the latter requires either the width or height of ImageView, or something like maxHeight, then the other property will follow accordingly. This property is more suitable for lists, like an activity list in an app. To ensure that high resolution pictures won’t be stretched or distorted on different screens, set the width of the picture to match_parent, and the height to wrap_content so it adjusts itself. (Note: It is best to put a default picture with the same size in the project resource files as a placeholder. This can avoid the poor user experience caused by pictures having a height of zero before being displayed.

Continuously updated

That’s all for now. I will continue to add to the post over time, so keep an eye out for updates. (Of course, if you have some efficient but rarely seen API, please share with me!)


This post was translated to English by Codementor’s content team. Here’s the original Chinese post by Yifeng, Android developer.

Discover and read more posts from Yi Feng
get started
Enjoy this post?

Leave a like and comment for Yi

9