Android Facebook like Custom ListView Feed using Volley
来源:互联网 发布:软件资格水平考试成绩 编辑:程序博客网 时间:2024/06/03 21:21
http://www.androidhive.info/2014/06/android-facebook-like-custom-listview-feed-using-volley/
My previous tutorial AndroidCustom ListViewcovers 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 id, profilepic, profile name, timestamp, statusmessage 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 articleAndroid 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 app, adapter, data 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.
- Android Facebook like Custom ListView Feed using Volley
- Facebook新闻页ListView优化(Fast Rendering News Feed on Android)
- Android Using Volley
- Transmitting Network Data Using Volley -Implementing a Custom Request
- facebook like
- multipart data using Android Volley
- Facebook的feed格式设计
- Asynchronous HTTP Requests in Android Using Volley
- Asynchronous HTTP Requests in Android Using Volley
- Asynchronous HTTP Requests in Android Using Volley
- Asynchronous HTTP Requests in Android Using Volley
- Asynchronous HTTP Requests in Android Using Volley
- Asynchronous HTTP Requests in Android Using Volley
- android: SlidingDrawer 抽屉效果 (Gesture View Like Facebook Notification)
- Android网络框架-Volley实践 使用Volley打造自定义ListView
- Android网络框架-Volley实践 使用Volley打造自定义ListView
- Using Custom Draw
- Using custom client attributes
- 项目管理工具
- axis2访问
- Ubuntu挂载新硬盘
- Peersim 学习日记 03 关于配置文件
- PageRank Algorithm (1)
- Android Facebook like Custom ListView Feed using Volley
- html和xml中的空格问题
- IntelliJ Idea --------erlang
- opensaml parser
- 存储单位换算
- UVA 11235 Frequent Value RMQ
- “你当然是个胆小鬼。”昆仑叶哲华骄傲地说道
- JS内建方法
- IOCP模型与网络编程