Sunday, June 7, 2020

Material Design: Material Time Picker in Kotlin

Material Time Picker for developer who do not like default Material Time Picker that difficult to use for most users
Maven
<dependency>
  <groupId>com.akexorcist</groupId>
  <artifactId>snap-time-picker</artifactId>
  <version>1.0.0</version>
</dependency>
Gradle
implementation 'com.akexorcist:snap-time-picker:1.0.0'
Feature

  • iOS Time Picker like with Material Design style
  • Some text & color customization
  • Selectable time range support
  • ViewModel support for event callback with LiveData (See example)

Usage

  • Relevant class in SnapTimePicker
  • SnapTimePickerDialog - Main Class
  • TimeValue - Time data holder that contain hour and minute
  • TimeRange - Time range data holder that contain the range of time with start (TimeValue) and end (TimeValue)

To use the SnapTimePicker you have to create the SnapTimePickerDialog from builder
val dialog = SnapTimePickerDialog.Builder().build()
dialog.show(supportFragmentManager, tag)
To custom some text and color in TimePickerDialog.
SnapTimePickerDialog.Builder().apply {
    setTitle(R.string.title)
    setPrefix(R.string.time_suffix)
    setSuffix(R.string.time_prefix)
    setThemeColor(R.color.colorAccent)
    setTitleColor(R.color.colorWhite)
}.build().show(supportFragmentManager, tag)
To set pre-selected time and time range in TimePickerDialog.
SnapTimePickerDialog.Builder().apply {
    setPreselectedTime(TimeValue(2, 34))
    setSelectableTimeRange(TimeRange(TimeValue(2, 15), TimeValue(14, 30)))
}.build().show(supportFragmentManager, tag)
For event callback from SnapTimePicker, you have assign the listener after build the SnapTimePickerDialog from builder.
SnapTimePickerDialog.Builder().apply {
    //
}.build().apply{
    setListener { hour, minute ->
        // Do something when user selected the time
    }
}.show(supportFragmentManager, tag)
But use listener does not good enough if the app can work in portrait and landscape. To support screen orientation, call useViewModel() in SnapTimePickerDialog then observe the event callback from SnapTimePicker's ViewModel from SnapTimePickerUtil
SnapTimePickerDialog.Builder().apply {
    useViewModel()
}.build().show(supportFragmentManager, SnapTimePickerDialog.TAG)
SnapTimePickerUtil.observe(this) { selectedHour: Int, selectedMinute: Int ->
    onTimePicked(selectedHour, selectedMinute)
 Material Time Picker in kotlin

Friday, June 5, 2020

Pass Bitmap Data Between Activities in Android

Pass Bitmap Data Between Activities in Android

Start New Activity from Current Activity
    Intent anotherIntent = new Intent(this, anotherActivity.class);
    startActivity(anotherIntent);
    finish();
Start New Acticity from Current Activity With Passing Required Data
    Intent anotherIntent = new Intent(this, anotherActivity.class);
    anotherIntent.putExtra("key", "value");
    startActivity(anotherIntent);
    finish();
putExtra() method is used to send extra data from one activity to another.
Extract Data In Other Activity
    data = getIntent().getStringExtra("key");
getIntent() method returns the intent that started this activity.
getStringExtra() retrieves extended data from the intent.
Pass Bitmap Data Between Activities in Android

Now, it turns out that it’s not possible to pass Bitmap as extended data. It needs to be converted to byteArray.
Pass Bitamp as Extended Data
    ByteArrayOutputStream bStream = new ByteArrayOutputStream();
    bitmap.compress(Bitmap.CompressFormat.PNG, 100, bStream);
    byte[] byteArray = bStream.toByteArray();
    Intent anotherIntent = new Intent(this, anotherActivity.class);
    anotherIntent.putExtra("image", byteArray);
    startActivity(anotherIntent);
    finish();
Retrieve Bitmap in Other Activity
    Bitmap bmp;
    byte[] byteArray = getIntent().getByteArrayExtra("image");
    bmp = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length);

Wednesday, June 3, 2020

Android example: Pinch to Zoom Android ImageView in Android

Open up Android Studio and create a new project and give it a name, in our case we’ve named it (ImageZoom), choose API 16 as the minimum SDK, then choose a blank activity, click “Finish” and wait for Android Studio to build your project.
Pinch to Zoom Android ImageView in Android

Open up build.gradle (Module:app) and add PhotoView library.
compile 'com.github.chrisbanes:PhotoView:2.1.3'
Now you need to open up build.gradle (Project) and you need to add this line maven { url "https://jitpack.io" } inside (allprojects) tag.
allprojects {
    repositories {
        google()
        jcenter()
        maven { url "https://jitpack.io" }
    }
}
Sync the project by clicking on (Sync Now).
Open activity_main.xml file, here you will add 3 views:
– Android ImageView that will hold the picture.
– Android TextView for the image title and description.
Add Android ImageView and make sure that you have added the image that you want to use for the ImageView inside the project drawable folder.
< ImageView
    android:id="@+id/ivIcon"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:srcCompat="@drawable/nature" />
Reduce the size of the ImageView by adjusting android:layout_width and android:layout_height
< ImageView
    android:id="@+id/ivIcon"
    android:layout_width="80dp"
    android:layout_height="80dp"
    app:srcCompat="@drawable/nature" />

Add some margin above the ImageView and place it in the center.
< ImageView
    android:id="@+id/ivIcon"
    android:layout_width="80dp"
    android:layout_height="80dp"
    android:layout_centerHorizontal="true"
    android:layout_marginTop="10dp"
    app:srcCompat="@drawable/nature" />
When you tap on the ImageView inside Android Studio preview window, you will see an empty space from top and bottom of the ImageView. You can fix that by using android:scaleType="centerCrop" which allow the image to fill the remaining space.
< ImageView
    android:id="@+id/ivIcon"
    android:layout_width="80dp"
    android:layout_height="80dp"
    android:layout_centerHorizontal="true"
    android:layout_marginTop="10dp"
    android:scaleType="centerCrop"
    app:srcCompat="@drawable/nature" />
Add Android TextView which will hold the name of the picture, this TextView will be placed below ImageView (ivIcon) in the center, add some margin from the top, try to increase text size to (20sp) and change text style to (italic and bold).
< TextView
    android:id="@+id/tvName"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="10dp"
    android:layout_below="@+id/ivIcon"
    android:text="@string/flower_name"
    android:layout_centerHorizontal="true"
    android:textSize="20sp"
    android:textStyle="italic|bold" />
Now you add the final TextView for this layout which will hold the description about the picture, this TextView will be placed below (tvName), try to increase text size to (16sp) and don’t forget to add some margin from the top so that it doesn’t appear to close with (tvName) TextView.
< TextView
    android:id="@+id/textView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_below="@+id/tvName"
    android:textSize="16sp"
    android:layout_marginTop="5dp"
    android:text="@string/flower_description" />
 Now you are done working on activity_main.xml file, next you need to open up MainActivity.java file and initialize Android ImageView and Android AlertDialog.
Initialize ImageView (ivIcon).
ImageView mIcon = findViewById(R.id.ivIcon);
Next you need to change the shape of ImageView (ivIcon) to circle using RoundedBitmapDrawable and Bitmap.
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.nature);
RoundedBitmapDrawable mDrawable = RoundedBitmapDrawableFactory.create(getResources(), bitmap);
mDrawable.setCircular(true);
mIcon.setImageDrawable(mDrawable);
Build and run the app to see the progress.
 Now you need to work on Android AlertDialog, the AlertDialog will appear on the screen when you try to tap on ImageView (ivIcon). Before you start that, you first need to create another layout file that you will use it later for AlertDialog.
 Create a new layout file and name it (dialog_custom_layout.xml), this file will have a PhotoView that will match the width and height of the screen.
< com.github.chrisbanes.photoview.PhotoView
    android:id="@+id/imageView"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
Now go back to MainActivity.java file, to be able to tap on the ImageView you will need to use setOnClickListener and inside onClick is where you will create AlertDialog.
mIcon.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                AlertDialog.Builder mBuilder = new AlertDialog.Builder(MainActivity.this);
                View mView = getLayoutInflater().inflate(R.layout.dialog_custom_layout, null);
                PhotoView photoView = mView.findViewById(R.id.imageView);
                photoView.setImageResource(R.drawable.nature);
                mBuilder.setView(mView);
                AlertDialog mDialog = mBuilder.create();
                mDialog.show();
            }
        });
Build and run the app to test out Android pinch and zoom function.
Hmm it seems that the image doesn’t fill the entire AlertDialog! You can actually fix it by using android:scaleType="centerCrop".
< com.github.chrisbanes.photoview.PhotoView
    android:id="@+id/imageView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:scaleType="centerCrop" />

Tuesday, June 2, 2020

Android Example: RecyclerView StaggeredGridLayoutManager Example

Android Example: RecyclerView StaggeredGridLayoutManager Example

1.1 What is RecyclerView?
The RecyclerView widget is a more advanced and flexible version of ListView.
1.2 Why RecyclerView?
RecyclerView is a container for displaying large data sets that can be scrolled very efficiently by maintaining a limited number of views.
1.3 When you should use RecyclerView?
You can use the RecyclerView widget when you have data collections whose elements changes at runtime based on user action or network events.
RecyclerView StaggeredGridLayoutManager Example

Step 1 Create Layout Files
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/rl"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    tools:context=".MainActivity"
    android:background="#ffffff"
    >
    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="vertical"
        >
    </android.support.v7.widget.RecyclerView>
</RelativeLayout>
Step 2 Create Layout Files
custom_view.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:id="@+id/card_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    card_view:cardCornerRadius="4dp"
    card_view:cardMaxElevation="2dp"
    card_view:cardElevation="1dp"
    >
    <TextView
        android:id="@+id/tv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textColor="#000"
        android:textSize="20dp"
        android:layout_margin="2dp"
        android:padding="5dp"
        android:layout_gravity="center"
        android:gravity="center"
        android:fontFamily="sans-serif-condensed"
        />
</android.support.v7.widget.CardView>
Step 3 Create class Files
MainActivity.java
public class MainActivity extends AppCompatActivity {
    private Context mContext;
    RelativeLayout mRelativeLayout;
    private RecyclerView mRecyclerView;
    private RecyclerView.Adapter mAdapter;
    private RecyclerView.LayoutManager mLayoutManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // Request window feature action bar
        requestWindowFeature(Window.FEATURE_ACTION_BAR);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // Get the application context
        mContext = getApplicationContext();
        // Change the action bar color
        getSupportActionBar().setBackgroundDrawable(new ColorDrawable(Color.RED));
        // Get the widgets reference from XML layout
        mRelativeLayout = (RelativeLayout) findViewById(R.id.rl);
        mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
        // Initialize a new String array
        String[] colors = {
                "Red","Green","Blue","Yellow","Magenta","Cyan","Orange",
                "Aqua","Azure","Beige","Bisque","Brown","Coral","Crimson"
        };
        /*
            StaggeredGridLayoutManager
                A LayoutManager that lays out children in a staggered grid formation. It supports
                horizontal & vertical layout as well as an ability to layout children in reverse.
                Staggered grids are likely to have gaps at the edges of the layout. To avoid these
                gaps, StaggeredGridLayoutManager can offset spans independently or move items
                between spans. You can control this behavior via setGapStrategy(int).
        */
        /*
            public StaggeredGridLayoutManager (int spanCount, int orientation)
                Creates a StaggeredGridLayoutManager with given parameters.
            Parameters
                spanCount : If orientation is vertical, spanCount is number of columns.
                    If orientation is horizontal, spanCount is number of rows.
                orientation : VERTICAL or HORIZONTAL
        */
        // Define a layout for RecyclerView
        mLayoutManager = new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL);
        mRecyclerView.setLayoutManager(mLayoutManager);
        // Initialize a new instance of RecyclerView Adapter instance
        mAdapter = new ColorAdapter(mContext,colors);
        // Set the adapter for RecyclerView
        mRecyclerView.setAdapter(mAdapter);
   }
}
Step 4 Create class Files
ColorAdapter.java
public class ColorAdapter extends RecyclerView.Adapter<ColorAdapter.ViewHolder>{
    private String[] mDataSet;
    private Context mContext;
    private Random mRandom = new Random();
    public ColorAdapter(Context context,String[] DataSet){
        mDataSet = DataSet;
        mContext = context;
    }
    public static class ViewHolder extends RecyclerView.ViewHolder{
        public TextView mTextView;
        public ViewHolder(View v){
            super(v);
            mTextView = (TextView)v.findViewById(R.id.tv);
        }
    }
    @Override
    public ColorAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType){
        // Create a new View
        View v = LayoutInflater.from(mContext).inflate(R.layout.custom_view,parent,false);
        ViewHolder vh = new ViewHolder(v);
        return vh;
    }
    @Override
    public void onBindViewHolder(ViewHolder holder, int position){
        holder.mTextView.setText(mDataSet[position]);
        // Set a random height for TextView
        holder.mTextView.getLayoutParams().height = getRandomIntInRange(250,75);
        // Set a random color for TextView background
        holder.mTextView.setBackgroundColor(getRandomHSVColor());
    }
    @Override
    public int getItemCount(){
        return mDataSet.length;
    }
    // Custom method to get a random number between a range
    protected int getRandomIntInRange(int max, int min){
        return mRandom.nextInt((max-min)+min)+min;
    }
    // Custom method to generate random HSV color
    protected int getRandomHSVColor(){
        // Generate a random hue value between 0 to 360
        int hue = mRandom.nextInt(361);
        // We make the color depth full
        float saturation = 1.0f;
        // We make a full bright color
        float value = 1.0f;
        // We avoid color transparency
        int alpha = 255;
        // Finally, generate the color
        int color = Color.HSVToColor(alpha, new float[]{hue, saturation, value});
        // Return the color
        return color;
    }
}
Step 5 import file build.gradle
  implementation 'com.android.support:cardview-v7:28.0.0'
  implementation 'com.android.support:recyclerview-v7:28.0.0'