Android Facebook like Custom ListView Feed using Volley

http://www.androidhive.info/2014/06/android-facebook-like-custom-listview-feed-using-volley/

 

My previous tutorial AndroidCustom ListView covers customizing android list view withimage and text. In this tutorial I am going to explain even more customizedlist view like Facebook, Google+ news feed where it contains multiple imagesand texts.

I used android volley networklibrary to make http calls and download the images. As volley comes withpowerful image caching mechanism, we don’t have to much worry about caching therequests and images.

DOWNLOAD CODE

Final Output

Following is the screenshot of final output of this tutorial.

 

Example feed Json

To demonstrate this tutorial I created an example json feedwhich contains an array of json feed object. Each object defines single feedrow where it contains information like feed idprofilepicprofile nametimestampstatusmessage and feedimage.

 

 

{

    "feed": [

        {

            "id": 1,

            "name": "National Geographic Channel",

            "image": "http://api.androidhive.info/feed/img/cosmos.jpg",

            "status": "\"Science is a beautiful and emotional human endeavor,\" says Brannon Braga, executive producer and director. \"And Cosmos is all about making science an experience.\"",

            "profilePic": "http://api.androidhive.info/feed/img/nat.jpg",

            "timeStamp": "1403375851930",

            "url": null

        },

        {

            "id": 2,

            "name": "TIME",

            "image": "http://api.androidhive.info/feed/img/time_best.jpg",

            "status": "30 years of Cirque du Soleil's best photos",

            "profilePic": "http://api.androidhive.info/feed/img/time.png",

            "timeStamp": "1403375851930",

            "url": "http://ti.me/1qW8MLB"

        }

    ]

}

In real world scenario, this feed json should be generateddynamically by reading a database.

 

1. Planning the Layout

Before start writing the code, I would like to plan the layoutfirst. If you observe the feed view, it has information like name,timestamp,profile pic, feed image and url. For this we need TextView, ImageView and alistview to display the feed. Using theLinearLayout’s orientation property I have alligned the elements vertically andhorizontally.

 

2. Downloading Volley.jar

If you are new to android volley library, I suggest you gothrough my previous article Android Volley Tutorial tounderstand what volley library is actually for. (In simple words volley is anetworking library used to make HTTP calls)

Download the volley.jar andkeep it aside.


Now let’s start by creating a new project.

3. Creating new project

1.Create a new project in Eclipse from File New Android Application Projectandfill all the required information.

2. Open res values dimens.xml andadd following dimensions. If you don’t have dimens.xml, create a new file andadd these values.

dimens.xml

<?xml version="1.0" encoding="utf-8"?>

<resources>

 

    <!-- Default screen margins, per the Android Design guidelines. -->

    <dimen name="activity_horizontal_margin">16dp</dimen>

    <dimen name="activity_vertical_margin">16dp</dimen>

     

     

    <dimen name="feed_item_margin">10dp</dimen>

    <dimen name="feed_item_padding_top_bottom">20dp</dimen>

    <dimen name="feed_item_padding_left_right">15dp</dimen>

    <dimen name="feed_item_profile_pic">50dp</dimen>

    <dimen name="feed_item_profile_info_padd">10dp</dimen>

    <dimen name="feed_item_profile_name">15dp</dimen>

    <dimen name="feed_item_timestamp">13dp</dimen>

    <dimen name="feed_item_status_pad_left_right">15dp</dimen>

    <dimen name="feed_item_status_pad_top">13dp</dimen>

    <dimen name="feed_item_corner_radius">3dp</dimen>

    <dimen name="feed_item_border_width">1dp</dimen>

 

</resources>

3. Alsocreate another xml file named colors.xml under res values andadd following colors.

colors.xml

<?xml version="1.0" encoding="utf-8"?>

<resources>

    <color name="white">#ffffff</color>

    <color name="feed_bg">#d3d6db</color>

    <color name="feed_item_bg">#ffffff</color>

    <color name="feed_item_border">#c2c3c8</color>

    <color name="link">#0a80d1</color>

    <color name="timestamp">#a0a3a7</color>   

</resources>

4. Under res drawable,create a new file called bg_parent_rounded_corner.xml and paste the below code. This xmlwill give a rounded corner background to feed item.

bg_parent_rounded_corner.xml

<?xml version="1.0" encoding="utf-8"?>

<shape xmlns:android="http://schemas.android.com/apk/res/android"

    android:shape="rectangle" >

 

    <!-- view background color -->

    <solid android:color="@color/feed_item_bg" >

    </solid>

 

    <!-- view border color and width -->

    <stroke

        android:width="@dimen/feed_item_border_width"

        android:color="@color/feed_item_border" >

    </stroke>

 

    <!-- Here is the corner radius -->

    <corners android:radius="@dimen/feed_item_corner_radius" >

    </corners>

 

</shape>

5. Tokeep the project well organized, I am creating required packages first. Create4 packages named appadapterdata and volley. To create newpackage, right click on src New Package andgive package name. Example: info.androidhive.listview.app.

Following is the project structure I am trying to achieve.

6. Nowpaste the volley.jar in project libs folder.

7.Create a class named LruBitmapCache.java under volley package and add the following code.This class takes care of caching network images on disk.

LruBitmapCache.java

package info.androidhive.listviewfeed.volley;

 

import com.android.volley.toolbox.ImageLoader.ImageCache;

 

import android.graphics.Bitmap;

import android.support.v4.util.LruCache;

 

public class LruBitmapCache extends LruCache<String, Bitmap> implements

        ImageCache {

    public static int getDefaultLruCacheSize() {

        final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);

        final int cacheSize = maxMemory / 8;

 

        return cacheSize;

    }

 

    public LruBitmapCache() {

        this(getDefaultLruCacheSize());

    }

 

    public LruBitmapCache(int sizeInKiloBytes) {

        super(sizeInKiloBytes);

    }

 

    @Override

    protected int sizeOf(String key, Bitmap value) {

        return value.getRowBytes() * value.getHeight() / 1024;

    }

 

    @Override

    public Bitmap getBitmap(String url) {

        return get(url);

    }

 

    @Override

    public void putBitmap(String url, Bitmap bitmap) {

        put(url, bitmap);

    }

}

8. Under app package create class named AppController.java and paste the following content. Thisis a singleton class which initializes global instances of required classes.All the objects related to volley are initialized here.

AppController.java

package info.androidhive.listviewfeed.app;

 

import info.androidhive.listviewfeed.volley.LruBitmapCache;

import android.app.Application;

import android.text.TextUtils;

 

import com.android.volley.Request;

import com.android.volley.RequestQueue;

import com.android.volley.toolbox.ImageLoader;

import com.android.volley.toolbox.Volley;

 

public class AppController extends Application {

 

    public static final String TAG = AppController.class.getSimpleName();

 

    private RequestQueue mRequestQueue;

    private ImageLoader mImageLoader;

    LruBitmapCache mLruBitmapCache;

 

    private static AppController mInstance;

 

    @Override

    public void onCreate() {

        super.onCreate();

        mInstance = this;

    }

 

    public static synchronized AppController getInstance() {

        return mInstance;

    }

 

    public RequestQueue getRequestQueue() {

        if (mRequestQueue == null) {

            mRequestQueue = Volley.newRequestQueue(getApplicationContext());

        }

 

        return mRequestQueue;

    }

 

    public ImageLoader getImageLoader() {

        getRequestQueue();

        if (mImageLoader == null) {

            getLruBitmapCache();

            mImageLoader = new ImageLoader(this.mRequestQueue, mLruBitmapCache);

        }

 

        return this.mImageLoader;

    }

 

    public LruBitmapCache getLruBitmapCache() {

        if (mLruBitmapCache == null)

            mLruBitmapCache = new LruBitmapCache();

        return this.mLruBitmapCache;

    }

 

    public <T> void addToRequestQueue(Request<T> req, String tag) {

        req.setTag(TextUtils.isEmpty(tag) ? TAG : tag);

        getRequestQueue().add(req);

    }

 

    public <T> void addToRequestQueue(Request<T> req) {

        req.setTag(TAG);

        getRequestQueue().add(req);

    }

 

    public void cancelPendingRequests(Object tag) {

        if (mRequestQueue != null) {

            mRequestQueue.cancelAll(tag);

        }

    }

}

9. Nowopen your AndroidManifest.xml file and add Application.java class in<application> tag. Also we need to add INTERNET permissionas this app makes network calls.

<application

    android:name="info.androidhive.listviewfeed.app.AppController"> ....</application>

AndroidManifest.xml

 

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

    package="info.androidhive.listviewfeed"

    android:versionCode="1"

    android:versionName="1.0" >

 

    <uses-sdk

        android:minSdkVersion="11"

        android:targetSdkVersion="19" />

 

    <uses-permission android:name="android.permission.INTERNET" />

 

    <application

        android:name="info.androidhive.listviewfeed.app.AppController"

        android:allowBackup="true"

        android:icon="@drawable/ic_launcher"

        android:label="@string/app_name"

        android:theme="@style/AppTheme" >

        <activity

            android:name="info.androidhive.listviewfeed.MainActivity"

            android:label="@string/app_name" >

            <intent-filter>

                <action android:name="android.intent.action.MAIN" />

 

                <category android:name="android.intent.category.LAUNCHER" />

            </intent-filter>

        </activity>

    </application>

 

</manifest>

 

10. Themain challenge in this project is adjusting the aspect ratio of feed image onceit is downloaded. Unfortunately volley doesn’t provide any callback method oncetheNetworkImageView is loaded. So I created a custom ImageView class with callback methods. Thisclass automatically adjusts the image height to prevent image aspect ratiodistortion.

Under your project main package create a class named FeedImageView.java and paste the below code. I took thecode from this stackoverflowanswer andmade few tweaks.

stackoverflowanswer: I'm trying out the Google's new Volley libraryand it's looking sharp and loads images quickly when I use this method setImageUrl:

holder.image.setImageUrl(url,ImageCacheManager.getInstance().getImageLoader());

I want to add toit a call back/listener method that will fire up when loading is finished, so Ican remove the progressBar view and show the image. It's an option that existsin Universal Image Loader and Picasso libraries, but for some reason I can't find a wayto do that in Volley, tried to Google different options but so far haven't found anyreference.

Does some has acode sample to illustrate how it's done?

 

You can use this FeedImageView in your xml layout like this

<info.androidhive.listviewfeed.FeedImageView

            android:id="@+id/feedImage1"

            android:layout_width="match_parent"

            android:layout_height="wrap_content"/>

 

FeedImageView.java

package info.androidhive.listviewfeed;

 

import android.content.Context;

import android.text.TextUtils;

import android.util.AttributeSet;

import android.widget.ImageView;

import android.widget.LinearLayout;

import android.widget.LinearLayout.LayoutParams;

 

import com.android.volley.VolleyError;

import com.android.volley.toolbox.ImageLoader;

import com.android.volley.toolbox.ImageLoader.ImageContainer;

import com.android.volley.toolbox.ImageLoader.ImageListener;

 

public class FeedImageView extends ImageView {

 

    public interface ResponseObserver {

        public void onError();

 

        public void onSuccess();

    }

 

    private ResponseObserver mObserver;

 

    public void setResponseObserver(ResponseObserver observer) {

        mObserver = observer;

    }

 

    /**

     * The URL of the network image to load

     */

    private String mUrl;

 

    /**

     * Resource ID of the image to be used as a placeholder until the network

     * image is loaded.

     */

    private int mDefaultImageId;

 

    /**

     * Resource ID of the image to be used if the network response fails.

     */

    private int mErrorImageId;

 

    /**

     * Local copy of the ImageLoader.

     */

    private ImageLoader mImageLoader;

 

    /**

     * Current ImageContainer. (either in-flight or finished)

     */

    private ImageContainer mImageContainer;

 

    public FeedImageView(Context context) {

        this(context, null);

    }

 

    public FeedImageView(Context context, AttributeSet attrs) {

        this(context, attrs, 0);

    }

 

    public FeedImageView(Context context, AttributeSet attrs,

            int defStyle) {

        super(context, attrs, defStyle);

    }

 

    /**

     * Sets URL of the image that should be loaded into this view. Note that

     * calling this will immediately either set the cached image (if available)

     * or the default image specified by

     * {@link VolleyImageView#setDefaultImageResId(int)} on the view.

     *

     * NOTE: If applicable, {@link VolleyImageView#setDefaultImageResId(int)}

     * and {@link VolleyImageView#setErrorImageResId(int)} should be called

     * prior to calling this function.

     *

     * @param url

     *            The URL that should be loaded into this ImageView.

     * @param imageLoader

     *            ImageLoader that will be used to make the request.

     */

    public void setImageUrl(String url, ImageLoader imageLoader) {

        mUrl = url;

        mImageLoader = imageLoader;

        // The URL has potentially changed. See if we need to load it.

        loadImageIfNecessary(false);

    }

 

    /**

     * Sets the default image resource ID to be used for this view until the

     * attempt to load it completes.

     */

    public void setDefaultImageResId(int defaultImage) {

        mDefaultImageId = defaultImage;

    }

 

    /**

     * Sets the error image resource ID to be used for this view in the event

     * that the image requested fails to load.

     */

    public void setErrorImageResId(int errorImage) {

        mErrorImageId = errorImage;

    }

 

    /**

     * Loads the image for the view if it isn't already loaded.

     *

     * @param isInLayoutPass

     *            True if this was invoked from a layout pass, false otherwise.

     */

    private void loadImageIfNecessary(final boolean isInLayoutPass) {

        final int width = getWidth();

        int height = getHeight();

 

        boolean isFullyWrapContent = getLayoutParams() != null

                && getLayoutParams().height == LayoutParams.WRAP_CONTENT

                && getLayoutParams().width == LayoutParams.WRAP_CONTENT;

        // if the view's bounds aren't known yet, and this is not a

        // wrap-content/wrap-content

        // view, hold off on loading the image.

        if (width == 0 && height == 0 && !isFullyWrapContent) {

            return;

        }

 

        // if the URL to be loaded in this view is empty, cancel any old

        // requests and clear the

        // currently loaded image.

        if (TextUtils.isEmpty(mUrl)) {

            if (mImageContainer != null) {

                mImageContainer.cancelRequest();

                mImageContainer = null;

            }

            setDefaultImageOrNull();

            return;

        }

 

        // if there was an old request in this view, check if it needs to be

        // canceled.

        if (mImageContainer != null && mImageContainer.getRequestUrl() != null) {

            if (mImageContainer.getRequestUrl().equals(mUrl)) {

                // if the request is from the same URL, return.

                return;

            } else {

                // if there is a pre-existing request, cancel it if it's

                // fetching a different URL.

                mImageContainer.cancelRequest();

                setDefaultImageOrNull();

            }

        }

 

        // The pre-existing content of this view didn't match the current URL.

        // Load the new image

        // from the network.

        ImageContainer newContainer = mImageLoader.get(mUrl,

                new ImageListener() {

                    @Override

                    public void onErrorResponse(VolleyError error) {

                        if (mErrorImageId != 0) {

                            setImageResource(mErrorImageId);

                        }

 

                        if (mObserver != null) {

                            mObserver.onError();

                        }

                    }

 

                    @Override

                    public void onResponse(final ImageContainer response,

                            boolean isImmediate) {

                        // If this was an immediate response that was delivered

                        // inside of a layout

                        // pass do not set the image immediately as it will

                        // trigger a requestLayout

                        // inside of a layout. Instead, defer setting the image

                        // by posting back to

                        // the main thread.

                        if (isImmediate && isInLayoutPass) {

                            post(new Runnable() {

                                @Override

                                public void run() {

                                    onResponse(response, false);

                                }

                            });

                            return;

                        }

 

                        int bWidth = 0, bHeight = 0;

                        if (response.getBitmap() != null) {

 

                            setImageBitmap(response.getBitmap());

                            bWidth = response.getBitmap().getWidth();

                            bHeight = response.getBitmap().getHeight();

                            adjustImageAspect(bWidth, bHeight);

 

                        } else if (mDefaultImageId != 0) {

                            setImageResource(mDefaultImageId);

                        }

 

                        if (mObserver != null) {

                            mObserver.onSuccess();

 

                        }

                    }

                });

 

        // update the ImageContainer to be the new bitmap container.

        mImageContainer = newContainer;

 

    }

 

    private void setDefaultImageOrNull() {

        if (mDefaultImageId != 0) {

            setImageResource(mDefaultImageId);

        } else {

            setImageBitmap(null);

        }

    }

 

    @Override

    protected void onLayout(boolean changed, int left, int top, int right,

            int bottom) {

        super.onLayout(changed, left, top, right, bottom);

        loadImageIfNecessary(true);

    }

 

    @Override

    protected void onDetachedFromWindow() {

        if (mImageContainer != null) {

            // If the view was bound to an image request, cancel it and clear

            // out the image from the view.

            mImageContainer.cancelRequest();

            setImageBitmap(null);

            // also clear out the container so we can reload the image if

            // necessary.

            mImageContainer = null;

        }

        super.onDetachedFromWindow();

    }

 

    @Override

    protected void drawableStateChanged() {

        super.drawableStateChanged();

        invalidate();

    }

 

    /*

     * Adjusting imageview height

     * */

    private void adjustImageAspect(int bWidth, int bHeight) {

        LinearLayout.LayoutParams params = (LayoutParams) getLayoutParams();

 

        if (bWidth == 0 || bHeight == 0)

            return;

 

        int swidth = getWidth();

        int new_height = 0;

        new_height = swidth * bHeight / bWidth;

        params.width = swidth;

        params.height = new_height;

        setLayoutParams(params);

    }

}

11. Openyour layout for main activity (activity_main.xml)and add a list view element for the feed list.

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="fill_parent"

    android:layout_height="fill_parent"

    android:orientation="vertical" >

 

    <ListView

        android:id="@+id/list"

        android:layout_width="fill_parent"

        android:layout_height="wrap_content"

        android:divider="@null" />

 

</LinearLayout>

12.Create another layout file named feed_item.xml under res layout folder.This layout file represents each individual feed item row in the list view.

feed_item.xml

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:background="@color/feed_bg"

    android:orientation="vertical" >

 

    <LinearLayout

        android:layout_width="fill_parent"

        android:layout_height="fill_parent"

 

        android:layout_marginLeft="@dimen/feed_item_margin"

        android:layout_marginRight="@dimen/feed_item_margin"

        android:layout_marginTop="@dimen/feed_item_margin"

        android:background="@drawable/bg_parent_rounded_corner"

        android:orientation="vertical"

        android:paddingBottom="@dimen/feed_item_padding_top_bottom"

        android:paddingTop="@dimen/feed_item_padding_top_bottom" >

 

        <LinearLayout

            android:layout_width="fill_parent"

            android:layout_height="wrap_content"

            android:orientation="horizontal"

            android:paddingLeft="@dimen/feed_item_padding_left_right"

            android:paddingRight="@dimen/feed_item_padding_left_right" >

 

            <com.android.volley.toolbox.NetworkImageView

                android:id="@+id/profilePic"

                android:layout_width="@dimen/feed_item_profile_pic"

                android:layout_height="@dimen/feed_item_profile_pic"

                android:scaleType="fitCenter" >

            </com.android.volley.toolbox.NetworkImageView>

 

            <LinearLayout

                android:layout_width="fill_parent"

                android:layout_height="wrap_content"

                android:orientation="vertical"

                android:paddingLeft="@dimen/feed_item_profile_info_padd" >

 

                <TextView

                    android:id="@+id/name"

                    android:layout_width="fill_parent"

                    android:layout_height="wrap_content"

                    android:textSize="@dimen/feed_item_profile_name"

                    android:textStyle="bold" />

 

                <TextView

                    android:id="@+id/timestamp"

                    android:layout_width="fill_parent"

                    android:layout_height="wrap_content"

                    android:textColor="@color/timestamp"

                    android:textSize="@dimen/feed_item_timestamp" />

            </LinearLayout>

        </LinearLayout>

 

        <TextView

            android:id="@+id/txtStatusMsg"

            android:layout_width="fill_parent"

            android:layout_height="wrap_content"

            android:paddingBottom="5dp"

            android:paddingLeft="@dimen/feed_item_status_pad_left_right"

            android:paddingRight="@dimen/feed_item_status_pad_left_right"

            android:paddingTop="@dimen/feed_item_status_pad_top" />

 

        <TextView

            android:id="@+id/txtUrl"

            android:layout_width="fill_parent"

            android:layout_height="wrap_content"

            android:linksClickable="true"

            android:paddingBottom="10dp"

            android:paddingLeft="@dimen/feed_item_status_pad_left_right"

            android:paddingRight="@dimen/feed_item_status_pad_left_right"

            android:textColorLink="@color/link" />

 

        <info.androidhive.listviewfeed.FeedImageView

            android:id="@+id/feedImage1"

            android:layout_width="match_parent"

            android:layout_height="wrap_content"

            android:background="@color/white"

            android:scaleType="fitXY"

            android:visibility="visible" />

    </LinearLayout>

 

</LinearLayout>

13. Under data package, create a class named FeedItem.java.This is a POJO class used to create objects for each feed item while parsingthe json. The feed item object contains information like profile pic, name,timestamp, status message, url and feed image.

FeedItem.java

package info.androidhive.listviewfeed.data;

 

public class FeedItem {

    private int id;

    private String name, status, image, profilePic, timeStamp, url;

 

    public FeedItem() {

    }

 

    public FeedItem(int id, String name, String image, String status,

            String profilePic, String timeStamp, String url) {

        super();

        this.id = id;

        this.name = name;

        this.image = image;

        this.status = status;

        this.profilePic = profilePic;

        this.timeStamp = timeStamp;

        this.url = url;

    }

 

    public int getId() {

        return id;

    }

 

    public void setId(int id) {

        this.id = id;

    }

 

    public String getName() {

        return name;

    }

 

    public void setName(String name) {

        this.name = name;

    }

 

    public String getImge() {

        return image;

    }

 

    public void setImge(String image) {

        this.image = image;

    }

 

    public String getStatus() {

        return status;

    }

 

    public void setStatus(String status) {

        this.status = status;

    }

 

    public String getProfilePic() {

        return profilePic;

    }

 

    public void setProfilePic(String profilePic) {

        this.profilePic = profilePic;

    }

 

    public String getTimeStamp() {

        return timeStamp;

    }

 

    public void setTimeStamp(String timeStamp) {

        this.timeStamp = timeStamp;

    }

 

    public String getUrl() {

        return url;

    }

 

    public void setUrl(String url) {

        this.url = url;

    }

}

14. Nowunder adapter package create a class named FeedListAdapter.java.This is a custom adapter class for feed list view. This adapter class takescare following things.

> Displayingfeed data like name, timestamp, profile pic, status message and feed image.
> 
Converts timestamp into x minutes/hours/days ago format
> Makes URLclickable byusingurl.setMovementMethod(LinkMovementMethod.getInstance())

FeedListAdapter.java

package info.androidhive.listviewfeed.adapter;

 

import info.androidhive.listviewfeed.FeedImageView;

import info.androidhive.listviewfeed.R;

import info.androidhive.listviewfeed.app.AppController;

import info.androidhive.listviewfeed.data.FeedItem;

 

import java.util.List;

 

import android.app.Activity;

import android.content.Context;

import android.text.Html;

import android.text.TextUtils;

import android.text.format.DateUtils;

import android.text.method.LinkMovementMethod;

import android.view.LayoutInflater;

import android.view.View;

import android.view.ViewGroup;

import android.widget.BaseAdapter;

import android.widget.TextView;

 

import com.android.volley.toolbox.ImageLoader;

import com.android.volley.toolbox.NetworkImageView;

 

public class FeedListAdapter extends BaseAdapter { 

    private Activity activity;

    private LayoutInflater inflater;

    private List<FeedItem> feedItems;

    ImageLoader imageLoader = AppController.getInstance().getImageLoader();

 

    public FeedListAdapter(Activity activity, List<FeedItem> feedItems) {

        this.activity = activity;

        this.feedItems = feedItems;

    }

 

    @Override

    public int getCount() {

        return feedItems.size();

    }

 

    @Override

    public Object getItem(int location) {

        return feedItems.get(location);

    }

 

    @Override

    public long getItemId(int position) {

        return position;

    }

 

    @Override

    public View getView(int position, View convertView, ViewGroup parent) {

 

        if (inflater == null)

            inflater = (LayoutInflater) activity

                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        if (convertView == null)

            convertView = inflater.inflate(R.layout.feed_item, null);

 

        if (imageLoader == null)

            imageLoader = AppController.getInstance().getImageLoader();

 

        TextView name = (TextView) convertView.findViewById(R.id.name);

        TextView timestamp = (TextView) convertView

                .findViewById(R.id.timestamp);

        TextView statusMsg = (TextView) convertView

                .findViewById(R.id.txtStatusMsg);

        TextView url = (TextView) convertView.findViewById(R.id.txtUrl);

        NetworkImageView profilePic = (NetworkImageView) convertView

                .findViewById(R.id.profilePic);

        FeedImageView feedImageView = (FeedImageView) convertView

                .findViewById(R.id.feedImage1);

 

        FeedItem item = feedItems.get(position);

 

        name.setText(item.getName());

 

        // Converting timestamp into x ago format

        CharSequence timeAgo = DateUtils.getRelativeTimeSpanString(

                Long.parseLong(item.getTimeStamp()),

                System.currentTimeMillis(), DateUtils.SECOND_IN_MILLIS);

        timestamp.setText(timeAgo);

 

        // Chcek for empty status message

        if (!TextUtils.isEmpty(item.getStatus())) {

            statusMsg.setText(item.getStatus());

            statusMsg.setVisibility(View.VISIBLE);

        } else {

            // status is empty, remove from view

            statusMsg.setVisibility(View.GONE);

        }

 

        // Checking for null feed url

        if (item.getUrl() != null) {

            url.setText(Html.fromHtml("<a href=\"" + item.getUrl() + "\">"

                    + item.getUrl() + "</a> "));

 

            // Making url clickable

            url.setMovementMethod(LinkMovementMethod.getInstance());

            url.setVisibility(View.VISIBLE);

        } else {

            // url is null, remove from the view

            url.setVisibility(View.GONE);

        }

 

        // user profile pic

        profilePic.setImageUrl(item.getProfilePic(), imageLoader);

 

        // Feed image

        if (item.getImge() != null) {

            feedImageView.setImageUrl(item.getImge(), imageLoader);

            feedImageView.setVisibility(View.VISIBLE);

            feedImageView

                    .setResponseObserver(new FeedImageView.ResponseObserver() {

                        @Override

                        public void onError() {

                        }

 

                        @Override

                        public void onSuccess() {

                        }

                    });

        } else {

            feedImageView.setVisibility(View.GONE);

        }

 

        return convertView;

    }

 

}

15. Nowwe have all the required classes in place. Open your main activity classMainActivity.java and add the following code. Here usingvolley JsonObjectRequest I fetched the json, parsed it(createdarray of feed item objects) and passed the data to list view adapter.

MainActivity.java

package info.androidhive.listviewfeed;

 

import info.androidhive.listviewfeed.adapter.FeedListAdapter;

import info.androidhive.listviewfeed.app.AppController;

import info.androidhive.listviewfeed.data.FeedItem;

 

import java.io.UnsupportedEncodingException;

import java.util.ArrayList;

import java.util.List;

 

import org.json.JSONArray;

import org.json.JSONException;

import org.json.JSONObject;

 

import android.annotation.SuppressLint;

import android.app.Activity;

import android.graphics.Color;

import android.graphics.drawable.ColorDrawable;

import android.os.Bundle;

import android.view.Menu;

import android.widget.ListView;

 

import com.android.volley.Cache;

import com.android.volley.Cache.Entry;

import com.android.volley.Request.Method;

import com.android.volley.Response;

import com.android.volley.VolleyError;

import com.android.volley.VolleyLog;

import com.android.volley.toolbox.JsonObjectRequest;

 

public class MainActivity extends Activity {

    private static final String TAG = MainActivity.class.getSimpleName();

    private ListView listView;

    private FeedListAdapter listAdapter;

    private List<FeedItem> feedItems;

    private String URL_FEED = "http://api.androidhive.info/feed/feed.json";

 

    @SuppressLint("NewApi")

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

 

        listView = (ListView) findViewById(R.id.list);

 

        feedItems = new ArrayList<FeedItem>();

 

        listAdapter = new FeedListAdapter(this, feedItems);

        listView.setAdapter(listAdapter);

         

        // These two lines not needed,

        // just to get the look of facebook (changing background color & hiding the icon)

        getActionBar().setBackgroundDrawable(new ColorDrawable(Color.parseColor("#3b5998")));

        getActionBar().setIcon(

                   new ColorDrawable(getResources().getColor(android.R.color.transparent)));

 

        // We first check for cached request

        Cache cache = AppController.getInstance().getRequestQueue().getCache();

        Entry entry = cache.get(URL_FEED);

        if (entry != null) {

            // fetch the data from cache

            try {

                String data = new String(entry.data, "UTF-8");

                try {

                    parseJsonFeed(new JSONObject(data));

                } catch (JSONException e) {

                    e.printStackTrace();

                }

            } catch (UnsupportedEncodingException e) {

                e.printStackTrace();

            }

 

        } else {

            // making fresh volley request and getting json

            JsonObjectRequest jsonReq = new JsonObjectRequest(Method.GET,

                    URL_FEED, null, new Response.Listener<JSONObject>() {

 

                        @Override

                        public void onResponse(JSONObject response) {

                            VolleyLog.d(TAG, "Response: " + response.toString());

                            if (response != null) {

                                parseJsonFeed(response);

                            }

                        }

                    }, new Response.ErrorListener() {

 

                        @Override

                        public void onErrorResponse(VolleyError error) {

                            VolleyLog.d(TAG, "Error: " + error.getMessage());

                        }

                    });

 

            // Adding request to volley request queue

            AppController.getInstance().addToRequestQueue(jsonReq);

        }

 

    }

 

    /**

     * Parsing json reponse and passing the data to feed view list adapter

     * */

    private void parseJsonFeed(JSONObject response) {

        try {

            JSONArray feedArray = response.getJSONArray("feed");

 

            for (int i = 0; i < feedArray.length(); i++) {

                JSONObject feedObj = (JSONObject) feedArray.get(i);

 

                FeedItem item = new FeedItem();

                item.setId(feedObj.getInt("id"));

                item.setName(feedObj.getString("name"));

 

                // Image might be null sometimes

                String image = feedObj.isNull("image") ? null : feedObj

                        .getString("image");

                item.setImge(image);

                item.setStatus(feedObj.getString("status"));

                item.setProfilePic(feedObj.getString("profilePic"));

                item.setTimeStamp(feedObj.getString("timeStamp"));

 

                // url might be null sometimes

                String feedUrl = feedObj.isNull("url") ? null : feedObj

                        .getString("url");

                item.setUrl(feedUrl);

 

                feedItems.add(item);

            }

 

            // notify data changes to list adapater

            listAdapter.notifyDataSetChanged();

        } catch (JSONException e) {

            e.printStackTrace();

        }

    }

 

    @Override

    public boolean onCreateOptionsMenu(Menu menu) {

        getMenuInflater().inflate(R.menu.main, menu);

        return true;

    }

 

}

Now run the project and test it once. Make sure that youremulator/device is having internet connect before you test.

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值