In this post, you will learn to create a simple
image slideshow app for Android. The user can select multiple images from the external card. Then by selecting the show menu item, the images will be displayed in slideshow view. The slideshow process will stop when all images are displayed.
Now, to develop the image slideshow app, you will need to create a new Android project. The project name is ImageSlider.
The first interface that you will see when the app starts up is the list of files and folders in the root directory. From this list, you can navigate to any sub-directory that contains the images to show. You can select or deselect multiple images as you want. The text box above the list displays the current directory. Making change to path in the text box will change the contents of the list. You can use this text box to navigate back and forth in the directory structure.
In this app, a class called
BrowseFragment that extends the Fragment class is used to display the list of files and folders from which the user can select images to show or navigate to a lower-level sub-folder. Its layout file (browse_view.xml) defines two components: one EditText and one ListView. The data source of the ListView that contains names of files and folders is set to the ListView by calling the listDirContents method of the MainActivity class. The MainActivity class is discussed later in this tutorial. Here are the contents of the BrowserFragment.java and browse_view.xml files.
BrowseFragment.java filepackage com.example.imageslider;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class BrowseFragment extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.browse_view, container, false);
}
public static BrowseFragment newInstance(String str){
return(new BrowseFragment());
}
public void onStart(){
super.onStart();
}
}
browse_view.xml file<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:orientation="vertical"
tools:context=".MainActivity" >
<EditText
android:id="@+id/txt_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/text_hint" />
<ListView
android:id="@+id/files_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
</ListView>
</LinearLayout>
Each item of the ListView contains text and check box. The text represents a file or folder. The check box allows the user to select a file or multiple files. If a folder is selected, the files and sub-folders in that folder will be listed. The file that defines the layout of list item is called mulliststyle.xml in the
res/layout directory.
mulliststyle.xml<?xml version="1.0" encoding="utf-8"?>
<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/text1"
android:layout_width="fill_parent"
android:layout_height="?android:attr/listPreferredItemHeight"
android:textAppearance="?android:attr/textAppearanceLarge"
android:gravity="center_vertical"
android:checkMark="?android:attr/listChoiceIndicatorMultiple"
android:paddingLeft="6dip"
android:paddingRight="6dip"
/>
When the user selects the
show menu item, the images will be displayed in slideshow view. You will need one more class that extends the Fragment class to display the image. This class is called
ContentFragment and its layout file is content_view.xml file. The content_view.xml file simply contains the blank LinearLayout container. The component that is used to show the image will be added to the container later by code.
ContentFragment.java filepackage com.example.imageslider;
import java.util.ArrayList;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.DisplayMetrics;
import android.view.LayoutInflater;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.SurfaceHolder.Callback;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.LinearLayout;
import android.widget.Toast;
import android.widget.AdapterView.OnItemClickListener;
public class ContentFragment extends Fragment{
private static Activity mAct;
private static ArrayList<String> args;
private ArrayList<Bitmap> bitmapRes;
private LinearLayout ll;
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
//Inflate the layout for this fragment
View v=inflater.inflate(R.layout.content_view, container, false);
ll=(LinearLayout) v.findViewById(R.id.layout_view);
//add SurfaceView representing the draw area to the layout
ll.addView(new MyDrawArea(this.getActivity()));
//Toast.makeText(this.getActivity(),"Hello="+ll.getWidth(), Toast.LENGTH_SHORT).show();
return ll;
}
public static ContentFragment newInstance(ArrayList<String> res){
args=res;
return(new ContentFragment());
}
public void onAttach(Activity activity){
super.onAttach(activity);
mAct=activity;
}
public void onStart(){
super.onStart();
//decode images
bitmapRes=processBitmap();
}
class ItemList implements OnItemClickListener{
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
Toast.makeText(mAct.getBaseContext(), "" + position, Toast.LENGTH_SHORT).show();
}
}
public ArrayList<Bitmap> processBitmap(){
ArrayList<Bitmap>lst=new ArrayList<Bitmap>();
//get screen dimension
DisplayMetrics metrics=new DisplayMetrics();
mAct.getWindowManager().getDefaultDisplay().getMetrics(metrics);
int rqwidth=metrics.widthPixels;
int rqheight=metrics.heightPixels;
//decode all images
for(int i=0;i<args.size();i++){
Bitmap image=decodeBitmap(args.get(i),rqwidth,rqheight);
lst.add(image);
}
return lst;
}
public Bitmap decodeBitmap(String path,int rqwidth,int rqheight){
BitmapFactory.Options option=new BitmapFactory.Options();
//specify decoding options
option.inJustDecodeBounds=true;
BitmapFactory.decodeFile(path,option);
option.inSampleSize=getSampleSize(option,rqwidth,rqheight);
option.inJustDecodeBounds=false;
return BitmapFactory.decodeFile(path,option);
}
public int getSampleSize(BitmapFactory.Options option, int rqwidth,int rqheight){
int samplesize=1;
int width=option.outWidth;
int height=option.outHeight;
if(width>rqwidth || height>rqheight){
int widthradio=Math.round((float)width/(float)rqwidth);
int heightradio=Math.round((float)height/(float)rqheight);
samplesize=widthradio<heightradio? widthradio:heightradio;
}
return samplesize;
}
//An image can be drawn on SurfaceView
class MyDrawArea extends SurfaceView implements Callback{
private Bitmap bitImage;
Paint p;
MyDrawArea(Context context){
super(context);
getHolder().addCallback(this);
getHolder().setFormat(PixelFormat.TRANSPARENT);
p=new Paint();
}
//This method will be called from the run method to show the image
public void drawImage(){
Canvas canvas = this.getHolder().lockCanvas();
canvas.drawColor(Color.BLACK);
canvas.drawBitmap(bitImage, 0, 0, p);
this.getHolder().unlockCanvasAndPost(canvas);
}
public void surfaceCreated(SurfaceHolder holder){
SlideThread thread=new SlideThread(holder,this);
thread.start(); //start images slide show
requestLayout();
}
public void surfaceDestroyed(SurfaceHolder holder){
}
public void surfaceChanged(SurfaceHolder holder,int format,int width,int height){
}
public void setBitmap(Bitmap bitmap){
bitImage=bitmap;
}
}
class SlideThread extends Thread{
MyDrawArea marea;
SlideThread(SurfaceHolder holder,MyDrawArea area){
marea=area;
}
public void run(){
for(int i=0;i<bitmapRes.size();i++){
try{
marea.setBitmap(bitmapRes.get(i)); //set the image to show on the drawing area
marea.drawImage(); //call the drawImage method to show the image
Thread.sleep(2200); //delay each image show
}catch(InterruptedException ie){}
}
}
}
}
content_view.xml file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/layout_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:orientation="horizontal"
/>
In the ContentFragment class, the drawing area object (
MyDrawArea class extended SurfaceView class) is added to layout so that the images can be displayed. The SurfaceView class has a method called
surfaceCreated. In this method, you will write code to show an images in slideshow view. The
SlideThread extends the Thread class handles the image slideshow process. This thread will start from the surfaceCreated method. The
run method of the SlideThread class is called when the thread starts. It loops throught the ArrayList object that contains the decoded images. The delay time between images show is specified by the
sleep method of the Thread class. While the loop is working the drawImage method is called to show the image on the drawing area.
The ArrayList bitmapRes is used to store the decoded images. The
processImage method is called when the ContentFragment starts. The processImage method will decode the images and add them to the bitmapRes. When decoding the image, you can specify a single dimension for all images to fit the screen. It is not good to display an 1050 x 1000 image on the 240 x 432 screen.
The
MainActivity class that extends the FragmentActivity class will be used as the container of the BrowseFragment and ContentFragment. When the app firstly starts, BrowseFragment is added to container to display list of files and folders. The BrowseFragment is replaced by the ContentFragment when the user touches the show menu item to display the images. Below are the content of the MainActivity.java file and the layout file of the MainActivity class.
MainActivity.java filepackage com.example.imageslider;
import java.io.File;
import java.util.ArrayList;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentTransaction;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
public class MainActivity extends FragmentActivity {
ArrayList<String> lstcheckeditems;
int mindex=0;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState != null) {
return;
}
//display the browse fragment to show the list of files and folders
BrowseFragment bf=new BrowseFragment();
FragmentTransaction transact=getSupportFragmentManager().beginTransaction();
transact.add(R.id.fragment_container,bf);
transact.addToBackStack(null);
transact.commit();
}
public void onBackPressed() {
LinearLayout view = (LinearLayout) findViewById(R.id.layout_view);
if(view!=null){
BrowseFragment df=new BrowseFragment();
FragmentTransaction transact=getSupportFragmentManager().beginTransaction();
transact.replace(R.id.fragment_container, df);
transact.addToBackStack(null);
transact.commit();
onStart();
}
else
System.exit(0);
}
public void onStart(){
super.onStart();
regControls();
lstcheckeditems=new ArrayList<String>() ;
}
public void regControls(){
EditText et=(EditText) findViewById(R.id.txt_input);
et.addTextChangedListener(new ChangeListener());
et.setText("/");
ListView lv=(ListView) findViewById(R.id.files_list);
lv.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
lv.setOnItemClickListener(new ClickListener());
}
class ChangeListener implements TextWatcher{
public void beforeTextChanged(CharSequence s, int start, int before, int count){
}
public void onTextChanged(CharSequence s, int start, int before, int count){
EditText et=(EditText) findViewById(R.id.txt_input);
String txt=et.getText().toString();
listDirContents(txt);
}
public void afterTextChanged(Editable ed){
}
}
class ClickListener implements OnItemClickListener{
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// selected item
String selectedItem = ((TextView) view).getText().toString();
EditText et=(EditText) findViewById(R.id.txt_input);
String path=et.getText().toString()+"/"+selectedItem;
File f=new File(path);
if(f.isDirectory())
et.setText(path);
else if(f.isFile() && (path.endsWith(".jpg") || path.endsWith(".png") || path.endsWith(".gif")))
{
if(lstcheckeditems.contains(path))
lstcheckeditems.remove(path);
else
lstcheckeditems.add(path);
}
}
public void onNothingSelected(AdapterView<?> parent){
}
}
public void listDirContents(String path){
ListView l=(ListView) findViewById(R.id.files_list);
if(path!=null){
try{
File f=new File(path);
if(f!=null){
String[] contents=f.list();
if(contents.length>0){
ArrayAdapter<String> aa=new ArrayAdapter<String>(this,R.layout.muliststyle,contents);
l.setAdapter(aa);
}
}
}catch(Exception e){}
}
}
public void show(ArrayList<String> res){
if(res.size()>0){
ContentFragment cf=ContentFragment.newInstance(res);
FragmentTransaction transact=getSupportFragmentManager().beginTransaction();
transact.replace(R.id.fragment_container, cf);
transact.addToBackStack(null);
transact.commit();
}
}
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
public boolean onOptionsItemSelected(MenuItem item)
{
switch (item.getItemId())
{
case R.id.menu_show:
show(lstcheckeditems);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
}
activity_main.xml file
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
The
regControls method registers the text change event to the EditText component and the click event to the ListView component so that the user can make change to the text box and select items of the list. It is called when the main activity starts.
The
listDirContents is also called when the main activity starts to read the files and folders from the root directory of Android device and show them in the list.
The
show method is called when the user touches the show menu item. This method will replace the BrowseFragment by the ContentFragment fragment on the MainActivity. Then the images slideshow works. You will need to edit the main.xml file in the
res/menu directory. This file defines the show menu item. The content of the main.xml file should look similar to the following:
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:id="@+id/menu_show"
android:title="Show" />
<item
android:id="@+id/action_settings"
android:orderInCategory="100"
android:showAsAction="never"
android:title="@string/action_settings"/>
</menu>
Now you are ready to run the program and test it. If you have questions, please leave them at the comment section. I will reply as soon as possible.