have a gridView populated by bitmaps that animate in when the bitmap loads asynchronously.
Sometimes when flinging the gridView some of the items do not render properly. The animation will trigger but the bitmap will simply not show up.
I have confirmed that the bitmap is indeed there (at least the data) but it just doesn't render.
This happens when flinging the gridView quickly but also happens on a slow scroll as well.
It seems that the view recycling is not working properly.
Here is my code:
ListView adapter:
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
if(convertView == null){
convertView = new FlipAnimatedCacheableImage(mContext);
}
final ImageInfo info = mapItem(getItem(position));
FlipAnimatedCacheableImage image = (FlipAnimatedCacheableImage)convertView;
image.resetState();
image.setTitle(info.title);
image.setSubTitle(info.subTitle);
image.loadImage(info.imgURL, false);
return convertView;
}
Code for FlipAnimatedCacheableImage:
public class FlipAnimatedCacheableImage extends FrameLayout
{
private static final String TAG = FlipAnimatedCacheableImage.class.getCanonicalName();
protected static final long DURATION = 300;
private ImageView mPlaceHolder;
private NetworkedCacheableImageView mCacheableImage;
private View mTextContainer;
private TextView mTitleTv;
private TextView mSubTitleTv;
private View mProgress;
private ImageLoadListener mListener = new ImageLoadListener()
{
private boolean isShown;
#Override
public void onImageLoaded(boolean animate)
{
if(animate){
mProgress.setVisibility(View.GONE);
mPlaceHolder.setVisibility(View.VISIBLE);
mPlaceHolder.setRotationY(0);
mCacheableImage.setVisibility(View.VISIBLE);
mCacheableImage.setRotationY(-90);
mPlaceHolder.animate().rotationY(90).setDuration(DURATION).start();
mCacheableImage.animate().rotationY(0).setDuration(DURATION).setStartDelay(DURATION).start();
if(!TextUtils.isEmpty(mTitleTv.getText()) || !TextUtils.isEmpty(mSubTitleTv.getText())){
mTextContainer.setVisibility(View.VISIBLE);
mTextContainer.setAlpha(0);
mTextContainer.animate().alpha(1).setDuration(DURATION).setStartDelay(DURATION * 2).start();
}
else{
mTextContainer.setVisibility(View.GONE);
}
isShown = true;
FlipAnimatedCacheableImage.this.invalidate();
}
else{
mPlaceHolder.setVisibility(View.GONE);
mProgress.setVisibility(View.GONE);
mCacheableImage.setVisibility(View.VISIBLE);
mCacheableImage.setRotationY(0);
mCacheableImage.clearAnimation();
if(!TextUtils.isEmpty(mTitleTv.getText()) || !TextUtils.isEmpty(mSubTitleTv.getText())){
mTextContainer.setVisibility(View.VISIBLE);
mTextContainer.setAlpha(1);
}
else{
mTextContainer.setVisibility(View.GONE);
}
FlipAnimatedCacheableImage.this.invalidate();
}
}
};
public FlipAnimatedCacheableImage(Context context, boolean isLarge)
{
this(context, null, 0, isLarge);
}
public FlipAnimatedCacheableImage(Context context)
{
this(context, null);
}
public FlipAnimatedCacheableImage(Context context, AttributeSet attrs)
{
this(context, attrs, 0, false);
}
public FlipAnimatedCacheableImage(Context context, AttributeSet attrs, int defStyle, boolean isLarge)
{
super(context, attrs, defStyle);
LayoutInflater inflater = LayoutInflater.from(context);
inflater.inflate(R.layout.item_image_thumbnail, this);
mCacheableImage = (NetworkedCacheableImageView)this.findViewById(R.id.image_view);
mPlaceHolder = (ImageView)this.findViewById(R.id.place_holder);
mProgress = this.findViewById(R.id.progressBar);
// only used by small images
mTextContainer = this.findViewById(R.id.text_container);
mTitleTv = (TextView)this.findViewById(R.id.text_title);
mSubTitleTv = (TextView)this.findViewById(R.id.text_sub_title);
// listener to animate after loading
mCacheableImage.setLoadListener(mListener);
// set default state
mTextContainer.setVisibility(View.GONE);
mTitleTv.setVisibility(GONE);
mSubTitleTv.setVisibility(GONE);
if(isLarge){
// adjust the size to the correct dimensions, ignore titleAnd subTitle
FrameLayout.LayoutParams imageParams = new FrameLayout.LayoutParams((int)context.getResources()
.getDimension(R.dimen.grid_image_width_large), (int)context.getResources().getDimension(
R.dimen.grid_image_height_large));
findViewById(R.id.content_wrapper).setLayoutParams(imageParams);
}
// this.setOnClickListener(listener);
resetState();
}
public void resetState()
{
mCacheableImage.setVisibility(View.VISIBLE);
mCacheableImage.setRotationY(0);
mProgress.setVisibility(View.VISIBLE);
mPlaceHolder.setVisibility(View.VISIBLE);
mTextContainer.setVisibility(View.GONE);
mTitleTv.setVisibility(GONE);
mSubTitleTv.setVisibility(GONE);
}
public boolean loadImage(String url, boolean fullSize)
{
return mCacheableImage.loadImage(url, fullSize);
}
public void setTitle(String title)
{
mTitleTv.setText(title);
if(!TextUtils.isEmpty(title)){
mTitleTv.setVisibility(View.VISIBLE);
}
else{
mTitleTv.setVisibility(View.GONE);
}
}
public void setSubTitle(String subTitle)
{
mSubTitleTv.setText(subTitle);
if(!TextUtils.isEmpty(subTitle)){
mSubTitleTv.setVisibility(View.VISIBLE);
}
else{
mSubTitleTv.setVisibility(View.GONE);
}
}
}
FlipAnimatedCache will request the image from a cache written by Chris Banes here https://github.com/chrisbanes/Android-BitmapCache
Here is that code:
/*******************************************************************************
* Copyright 2011, 2013 Chris Banes.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/
/**
* Simple extension of CacheableImageView which allows downloading of Images of
* the Internet.
*
* This code isn't production quality, but works well enough for this sample.s
*
* #author Chris Banes
*
*/
public class NetworkedCacheableImageView extends CacheableImageView
{
private static final String TAG = NetworkedCacheableImageView.class.getCanonicalName();
public interface ImageLoadListener
{
public void onImageLoaded(boolean animate);
}
/**
* This task simply fetches an Bitmap from the specified URL and wraps it in
* a wrapper. This implementation is NOT 'best practice' or production ready
* code.
*/
private static class ImageUrlAsyncTask extends AsyncTask<String, Void, CacheableBitmapDrawable>
{
private final BitmapLruCache mCache;
private final WeakReference<ImageView> mImageViewRef;
private final WeakReference<NetworkedCacheableImageView> viewRef;
private final BitmapFactory.Options mDecodeOpts;
private final ImageLoadListener mLoadListener;
private boolean outOfMemoryFailure;
private String mURL;
ImageUrlAsyncTask(ImageView imageView, BitmapLruCache cache, BitmapFactory.Options decodeOpts,
ImageLoadListener listener, NetworkedCacheableImageView view)
{
mCache = cache;
mLoadListener = listener;
mImageViewRef = new WeakReference<ImageView>(imageView);
viewRef = new WeakReference<NetworkedCacheableImageView>(view);
mDecodeOpts = decodeOpts;
}
#Override
protected CacheableBitmapDrawable doInBackground(String... params)
{
try{
// Return early if the ImageView has disappeared.
if(null == mImageViewRef.get()){
return null;
}
mURL = params[0];
// Now we're not on the main thread we can check all caches
CacheableBitmapDrawable result = mCache.get(mURL, mDecodeOpts);
if(null == result){
Log.w("CACHE", "Downloading: " + mURL);
// The bitmap isn't cached so download from the web
HttpURLConnection conn = (HttpURLConnection)new URL(mURL).openConnection();
InputStream is = new BufferedInputStream(conn.getInputStream());
// Add to cache
result = mCache.put(mURL, is, mDecodeOpts);
}
else{
Log.w("CACHE", "Got from Disk Cache: " + mURL);
}
return result;
}
catch(IOException e){
Log.e("Error downloading image", e.toString());
}
catch(OutOfMemoryError e){
Log.e(TAG, "running out of memory, trimming image memory cache");
outOfMemoryFailure = true;
}
return null;
}
#Override
protected void onPostExecute(final CacheableBitmapDrawable result)
{
// super.onPostExecute(result);
if(outOfMemoryFailure || result == null || !result.hasValidBitmap()){
mCache.trimMemory();
// viewRef.get().loadImageAsync(mURL, mDecodeOpts);
Log.e(TAG, "image probably did not load::" + mURL);
}
else{
if(BuildConfig.DEBUG){
Log.d("bitmapCache", "NetworkedCacheableImageView.ImageUrlAsyncTask.onPostExecute() WIDTH:"
+ result.getBitmap().getWidth());
Log.d("bitmapCache", "NetworkedCacheableImageView.ImageUrlAsyncTask.onPostExecute() HEIGHT:"
+ result.getBitmap().getHeight());
}
Log.i(TAG, "RESULT for :" + mURL + " mImageViewRef::" + mImageViewRef + " outOfMemoryFailure::"
+ outOfMemoryFailure);
if(result != null){
Log.i(TAG, "RESULT object for :" + mURL + " result:hasValidBitmap" + result.hasValidBitmap()
+ " result:isReferencedByCache" + result.isReferencedByCache() + " result.isBeingDisplayed() "
+ result.isBeingDisplayed());
}
Runnable runnable = new Runnable()
{
#Override
public void run()
{
if(mImageViewRef != null){
final ImageView iv = mImageViewRef.get();
Log.e(TAG, "RESULT image view reference :" + mURL + " view ref::" + iv);
if(null != iv){
iv.setImageDrawable(result);
if(mLoadListener != null){
mLoadListener.onImageLoaded(true);
}
}
}
}
};
Handler handler = new Handler();
handler.postDelayed(runnable, 50);
}
super.onPostExecute(result);
}
}
private final BitmapLruCache mCache;
private ImageUrlAsyncTask mCurrentTask;
private ImageLoadListener mListener;
public NetworkedCacheableImageView(Context context, AttributeSet attrs)
{
super(context, attrs);
mCache = WatchApplication.getBitmapCache();
}
public void setLoadListener(ImageLoadListener listener)
{
mListener = listener;
}
public void removeListener()
{
mListener = null;
}
/**
* Loads the Bitmap.
*
* #param url
* - URL of image
* #param fullSize
* - Whether the image should be kept at the original size
* #return true if the bitmap was found in the cache
*/
public boolean loadImage(String url, final boolean fullSize)
{
setImageDrawable(null);
// First check whether there's already a task running, if so cancel it
if(TextUtils.isEmpty(url))
return false;
if(null != mCurrentTask){
mCurrentTask.cancel(false);
}
// Check to see if the memory cache already has the bitmap. We can
// safely do
// this on the main thread.
BitmapDrawable wrapper = mCache.getFromMemoryCache(url);
if(null != wrapper){
// The cache has it, so just display it
if(BuildConfig.DEBUG){
Log.w(TAG, "CACHE. FOUND IN MEMORY:" + url);
}
setImageDrawable(wrapper);
if(mListener != null){
mListener.onImageLoaded(false);
}
return true;
}
else{
// Memory Cache doesn't have the URL, do threaded request...
BitmapFactory.Options decodeOpts = null;
if(!fullSize){
decodeOpts = new BitmapFactory.Options();
// decodeOpts.inDensity = DisplayMetrics.DENSITY_XHIGH;
decodeOpts.inPurgeable = true;
decodeOpts.outHeight = this.getHeight();
decodeOpts.outWidth = this.getWidth();
}
loadImageAsync(url, decodeOpts);
return false;
}
}
public void loadImageAsync(String url, BitmapFactory.Options decodeOpts)
{
mCurrentTask = new ImageUrlAsyncTask(this, mCache, decodeOpts, mListener, this);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){
SDK11.executeOnThreadPool(mCurrentTask, url);
}
else{
mCurrentTask.execute(url);
}
}
}
Thanks in advance for any help!
Two things worth trying -
For chaining animation operations use a listener:
mPlaceHolder.animate()
.alpha(0f)
.scaleX(0.9f)
.scaleY(0.9f)
.rotationY(90)
.setDuration(DURATION)
.setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
mCacheableImage.setImageDrawable(drawable);
mCacheableImage.animate()
.alpha(1f)
.scaleY(1f)
.scaleX(1f)
.rotationY(0)
.setDuration(DURATION)
.setListener(null);
}
});
Use setHasTransientState() to ensure the views are not recycled in the ListView/GridView. See this DevByte video for more info.
Related
I am fetching data from JSON with volley. In the data displayed in bookContent, there are <img> tags in varying positions.
I'm using Universal Image Loader to Load the images in the <img> tags.
This is my Activity.
BookDetails
public class BookDetails extends AppCompatActivity{
private final String TAG = "BookDetails";
private JSONObject bookData;
protected com.nostra13.universalimageloader.core.ImageLoader mImageLoader;
TextView bookTitle, bookAuthorDate, bookContent;
View firstView, secView;
CircularNetworkImageView authorImg;
ImageLoader AuthImgLoader;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_book_details);
showDialog();
bookTitle = (TextView) findViewById(R.id.dbook_title);
bookAuthorDate = (TextView) findViewById(R.id.author_date);
bookContent = (TextView) findViewById(R.id.dbook_content);
authorImg = (CircularNetworkImageView) findViewById(R.id.author_img);
firstView = findViewById(R.id.dviewtop);
secView = findViewById(R.id.dviewbottom);
DisplayImageOptions defaultoptions = new DisplayImageOptions.Builder()
.cacheInMemory(true)
.cacheOnDisk(true)
.build();
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getApplicationContext())
.defaultDisplayImageOptions(defaultoptions)
.writeDebugLogs()
.build();
mImageLoader = com.nostra13.universalimageloader.core.ImageLoader.getInstance();
mImageLoader.init(config);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
}
if (savedInstanceState != null) {
try {
String bookDataStr = savedInstanceState.getString("bookData");
bookData = new JSONObject(bookDataStr);
parseBook(bookData);
} catch (JSONException e) {
e.printStackTrace();
}
} else {
if (NetworkCheck.isAvailableAndConnected(this)) {
//Calling method to load books
loadBook();
} else {
internetDialog.show();
}
}
}
private void loadBook() {
Log.d(TAG, "loadBook called");
final ProgressBar progressBar;
progressBar = (ProgressBar) findViewById(R.id.progress_circle);
progressBar.setVisibility(View.VISIBLE);
int news_id = getIntent().getIntExtra("BookId", -1);
Log.d(TAG, "You clicked book id " + book_id);
final JsonObjectRequest jsonObjReq = new JsonObjectRequest( DetailConfig.GET_DURL + book_id, null,
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
Log.d("Debug", response.toString());
//Dismissing progressbar;
if (progressBar != null) {
progressBar.setVisibility(View.GONE);
}
bookData = response;
//Calling method to parse json array
parseBook(response);
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
VolleyLog.d("", "Error: " + error.getMessage());
if (progressBar != null) {
progressBar.setVisibility(View.GONE);
}
}
});
//Creating request queue
RequestQueue requestQueue = Volley.newRequestQueue(this);
//Adding request to queue
requestQueue.add(jsonObjReq);
}
//This method will parse json data of book
private void parseBook(JSONObject jsonObject) {
Log.d(TAG, "Parsing book array");
try {
String title = jsonObject.getString(DetailConfig.TAG_DPOST_TITLE);
bookTitle.setText(Html.fromHtml(title));
JSONObject pAuthor = jsonObject.getJSONObject("author");
String author = pAuthor.getString("name");
String authorimg = pAuthor.getString("avatar");
AuthImgLoader = VolleyRequest.getInstance(getApplicationContext()).getImageLoader();
AuthImgLoader.get(authorimg, ImageLoader.getImageListener(authorImg, R.drawable.ic_author, R.drawable.ic_author));
authorImg.setImageUrl(authorimg, AuthImgLoader);
String content = jsonObject.getString(DetailConfig.TAG_DPOST_CONTENT);
Spanned spanned = Html.fromHtml(content, new UILImageGetter(bookContent, this), null);
bookContent.setText(spanned);
} catch (JSONException w) {
w.printStackTrace();
}
//Unhiding views
bookTitle.setVisibility(View.VISIBLE);
bookAuthorDate.setVisibility(View.VISIBLE);
bookContent.setVisibility(View.VISIBLE);
authorImg.setVisibility(View.VISIBLE);
firstView.setVisibility(View.VISIBLE);
secView.setVisibility(View.VISIBLE);
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("bookData", bookData.toString());
}
}
Below, I use this piece of code I got from the accepted answer in this question to load the images in bookContent.
This class uses Universal Image Loader.
UILImageGetter
public class UILImageGetter implements Html.ImageGetter{
Context c;
TextView conatiner;
UrlImageDownloader urlDrawable;
public UILImageGetter(View textView, Context context) {
this.c = context;
this.conatiner = (TextView) textView;
}
#Override
public Drawable getDrawable(String source) {
urlDrawable = new UrlImageDownloader(c.getResources(), source);
if (Build.VERSION.SDK_INT >= 21) {
urlDrawable.mDrawable = c.getResources().getDrawable(R.drawable.default_thumb,null);
} else {
urlDrawable.mDrawable = c.getResources().getDrawable(R.drawable.default_thumb);
}
ImageLoader.getInstance().loadImage(source, new SimpleListener(urlDrawable));
return urlDrawable;
}
private class SimpleListener extends SimpleImageLoadingListener {
UrlImageDownloader mUrlImageDownloader;
public SimpleListener(UrlImageDownloader downloader) {
super();
mUrlImageDownloader= downloader;
}
#Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
int width = loadedImage.getWidth();
int height = loadedImage.getHeight();
int newWidth = width;
int newHeight = height;
if (width > conatiner.getWidth()) {
newWidth = conatiner.getWidth();
newHeight = (newWidth * height) / width;
}
if (view != null) {
view.getLayoutParams().width = newWidth;
view.getLayoutParams().height = newHeight;
}
Drawable result = new BitmapDrawable(c.getResources(), loadedImage);
result.setBounds(0, 0, newWidth, newHeight);
mUrlImageDownloader.setBounds(0, 0, newWidth, newHeight);
mUrlImageDownloader.mDrawable = result;
conatiner.setHeight((conatiner.getHeight() + result.getIntrinsicHeight()));
conatiner.invalidate();
}
}
private class UrlImageDownloader extends BitmapDrawable {
public Drawable mDrawable;
public UrlImageDownloader(Resources resources, String filepath) {
super(resources, filepath);
mDrawable = new BitmapDrawable(resources, filepath);
}
#Override
public void draw(Canvas canvas) {
if (mDrawable != null) {
mDrawable.draw(canvas);
}
}
}
}
Everything works fine, the JSON is properly parsed and displayed, the images are loaded but there is a problem.
The loaded images are affecting the vertical lines that are displayed in bookContent. If there are many vertical lines, some part of it is cut off.
And if the bookContent has very few vertical lines, a large empty space is left at the bottom of the TextView.
However, if I don't load the images, the bookContent appears fine, no cut-offs, no extra space.
Please, how do I fix it?
I go the answer to this problem from dcow's comment in this question. What I did is that I removed
conatiner.setHeight((conatiner.getHeight() + result.getIntrinsicHeight()));
and wrote
container.setText(container.getText()); under
container.setText(container.getText());.
I have a question. At this time, the capturePicture of WebView is deprecated.
I want to ask if there is a way to replace the function. I meant it can capture entire of the webview (not only the view is displayed)
Thanks
I finally found out the solution.
Some of codes
public class WebViewActivity extends Activity {
private static WebView webView;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.webview);
webView = (WebView) findViewById(R.id.webView1);
webView.loadUrl("http://developer.android.com/reference/packages.html");
// webView.loadUrl("http://developer.android.com/training/basics/firstapp/creating-project.html");
webView.setWebViewClient(new WebViewClient() {
public void onPageFinished(WebView view, String url) {
// do your stuff here
webView.measure(MeasureSpec.makeMeasureSpec(
MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED),
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
webView.layout(0, 0, webView.getMeasuredWidth(),
webView.getMeasuredHeight());
webView.setDrawingCacheEnabled(true);
webView.buildDrawingCache();
Bitmap bm = Bitmap.createBitmap(webView.getMeasuredWidth(),
webView.getMeasuredHeight(), Bitmap.Config.ARGB_8888);
Canvas bigcanvas = new Canvas(bm);
Paint paint = new Paint();
int iHeight = bm.getHeight();
bigcanvas.drawBitmap(bm, 0, iHeight, paint);
webView.draw(bigcanvas);
System.out.println("1111111111111111111111="
+ bigcanvas.getWidth());
System.out.println("22222222222222222222222="
+ bigcanvas.getHeight());
if (bm != null) {
try {
String path = Environment.getExternalStorageDirectory()
.toString();
OutputStream fOut = null;
File file = new File(path, "/aaaa.png");
fOut = new FileOutputStream(file);
bm.compress(Bitmap.CompressFormat.PNG, 50, fOut);
fOut.flush();
fOut.close();
bm.recycle();
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
}
}
The layout.xml
<?xml version="1.0" encoding="utf-8"?>
<WebView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/webView1"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
Use draw() method of WebView
Example :
ImageView imageview;
WebView webview;
class Background extends AsyncTask<Void, Void, Bitmap>
{
#Override
protected Bitmap doInBackground(Void... params)
{
try
{
Thread.sleep(2000);
Bitmap bitmap = Bitmap.createBitmap(webview.getWidth(), webview.getHeight(), Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
webview.draw(canvas);
return bitmap;
}
catch (InterruptedException e){}
catch (Exception e){}
return null;
}
#Override
protected void onPostExecute(Bitmap result)
{
imageview.setImageBitmap(result);
}
}
webview.setWebChromeClient(new WebChromeClient()
{
public void onProgressChanged(WebView view, int progress)
{
if(progress==100)
new Background().execute();
}
});
Just extends BaseWebView below:
public abstract class BaseWebView extends WebView {
private static final String TAG = "BaseWebView";
private static final int DELAY_MS_MAX = 1000;
private static final int DELAY_MS_DRAW = 100;
private static final long REQUEST_ID_INIT = 0;
public BaseWebView(Context context) {
super(context);
init();
}
public BaseWebView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public BaseWebView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
/**
* to make sure to set {#link #loadDataFinished} to true at last
* in {#link #DELAY_MS_MAX} ms later.
*/
private static final int MSG_LOAD_DATA_FINISH_DEF = 0;
/**
* to set {#link #loadDataFinished} to true in {#link #DELAY_MS_DRAW} ms later.
*/
private static final int MSG_LOAD_DATA_FINISH_DRAW = 1;
private String description;
/**
* if load data finished
* <P><P>
* mark the status for load API {#link #loadData(String, String, String)}
* & {#link #loadDataWithBaseURL(String, String, String, String, String)}
* <p>
* <b>ignored:</b> {#link #loadUrl(String)}, {#link #loadUrl(String, Map)} and {#link #reload()}
*/
private AtomicBoolean loadDataFinished = new AtomicBoolean(true);
/**
* if progress reached 1000
* <P><P>
* mark the status for load API {#link #loadData(String, String, String)}
* & {#link #loadDataWithBaseURL(String, String, String, String, String)}
* <p>
* <b>ignored:</b> {#link #loadUrl(String)}, {#link #loadUrl(String, Map)} and {#link #reload()}
*/
private AtomicBoolean progressFinished = new AtomicBoolean(true);
/**
* the request id of load data
* <P><P>
* mark the status for load API {#link #loadData(String, String, String)}
* & {#link #loadDataWithBaseURL(String, String, String, String, String)}
* <p>
* <b>ignored:</b> {#link #loadUrl(String)}, {#link #loadUrl(String, Map)} and {#link #reload()}
*/
private AtomicLong loadDataTimestamp = new AtomicLong(REQUEST_ID_INIT);
private Handler handler = new MyHandler(this);
/**
* clear status for load API {#link #loadData(String, String, String)}
* & {#link #loadDataWithBaseURL(String, String, String, String, String)}, set to false.
* <p>
* <b>ignored:</b> {#link #loadUrl(String)}, {#link #loadUrl(String, Map)} and {#link #reload()}
* <p>
* <b>Usage:</b> call in load API {#link #loadData(String, String, String)}
* & {#link #loadDataWithBaseURL(String, String, String, String, String)}
*/
private void clearLoadDataStatus() {
DLog.d(TAG, "clearLoadDataStatus: set false, obj=" + this.hashCode());
loadDataFinished.set(false);
progressFinished.set(false);
// generates a new load request id
loadDataTimestamp.set(System.currentTimeMillis());
}
private void setLoadDataFinished(long requestId) {
DLog.d(TAG, "setLoadDataFinished: id=" + requestId
+ ", loadDataTimestamp=" + loadDataTimestamp.get() + ", obj=" + this.hashCode());
if (!progressFinished.get() || requestId != loadDataTimestamp.get()) {
return;
}
loadDataFinished();
}
private void loadDataFinished() {
DLog.d(TAG, "loadDataFinished: set true, obj=" + this.hashCode());
loadDataFinished.set(true);
// clear load request id
loadDataTimestamp.set(REQUEST_ID_INIT);
}
/**
* get status for load API {#link #loadData(String, String, String)}
* & {#link #loadDataWithBaseURL(String, String, String, String, String)}, set to false.
* <p>
* <b>ignored:</b> {#link #loadUrl(String)}, {#link #loadUrl(String, Map)} and {#link #reload()}
*/
public boolean isLoadDataFinished() {
return loadDataFinished.get();
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
private void handleLoadDataFinished(int what) {
if (loadDataTimestamp.get() == REQUEST_ID_INIT) {
// there is no load data actions
//DLog.w(TAG, "handleLoadDataFinished: there is no load data actions, obj="
// + this.hashCode());
return;
}
DLog.d(TAG, "handleLoadDataFinished: obj=" + this.hashCode());
int delay = 0;
long requestId = loadDataTimestamp.get();
if (what == MSG_LOAD_DATA_FINISH_DEF) {
delay = DELAY_MS_MAX;
} else if (what == MSG_LOAD_DATA_FINISH_DRAW) {
delay = DELAY_MS_DRAW;
}
handler.removeMessages(what);
handler.sendMessageDelayed(Message.obtain(handler, what, (Object) requestId), delay);
}
private void init() {
this.setWebChromeClient(new DefWebChromeClient());
this.setHorizontalScrollBarEnabled(false);
this.setVerticalScrollBarEnabled(false);
this.description = onGetDescription();
}
/**
* a description of this WebView
*/
abstract public String onGetDescription();
#Override
public void loadDataWithBaseURL(
#Nullable String baseUrl, String data, #Nullable String mimeType,
#Nullable String encoding, #Nullable String historyUrl) {
clearLoadDataStatus();
super.loadDataWithBaseURL(baseUrl, data, mimeType, encoding, historyUrl);
}
#Override
public void loadData(String data, #Nullable String mimeType, #Nullable String encoding) {
clearLoadDataStatus();
super.loadData(data, mimeType, encoding);
}
#Override
public void stopLoading() {
super.stopLoading();
loadDataFinished();
}
#Override
public void setWebChromeClient(WebChromeClient client) {
if (!(client instanceof DefWebChromeClient)) {
throw new IllegalArgumentException(
"the WebChromeClient must be an instance of " + DefWebChromeClient.class);
}
super.setWebChromeClient(client);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//Log.d(TAG, "onDraw: " + this.hashCode());
if (progressFinished.get()) {
// set load data finished in DELAY_MS_DRAW.
// !!! Notice that the Runnable will not be exec if this WebView is not to show,
// and this will cause the `loadDataFinished` being `false` all the time,
// if you want that executing whatever, plz use `handle.post()`.
post(new Runnable() {
#Override
public void run() {
handleLoadDataFinished(MSG_LOAD_DATA_FINISH_DRAW);
}
});
}
}
class DefWebChromeClient extends WebChromeClient {
#Override
public void onProgressChanged(WebView view, int newProgress) {
super.onProgressChanged(view, newProgress);
if (newProgress == 100) {
progressFinished.set(true);
// set load data finished in DELAY_MS_MAX as def.
// !!! Notice that the Runnable will not be exec if this WebView is not to show,
// and this will cause the `loadDataFinished` being `false` all the time,
// if you want that executing whatever, plz use `handle.post()`.
post(new Runnable() {
#Override
public void run() {
handleLoadDataFinished(MSG_LOAD_DATA_FINISH_DEF);
}
});
}
Log.d(TAG, "onProgressChanged: " + newProgress
+ ", desc=" + description + ", obj=" + view.hashCode());
}
}
static class MyHandler extends Handler {
WeakReference<BaseWebView> mViewReference;
MyHandler(BaseWebView webView) {
mViewReference = new WeakReference<>(webView);
}
#Override
public void handleMessage(Message msg) {
final BaseWebView webView = mViewReference.get();
if (webView == null) {
return;
}
// handle msg
if (msg.what == MSG_LOAD_DATA_FINISH_DEF || msg.what == MSG_LOAD_DATA_FINISH_DRAW) {
webView.setLoadDataFinished((Long) msg.obj);
}
}
}
}
=== END ===
Notice that only loadData() & loadDataWithBaseURL() works above,
you can add loadUrl(), reload() by yourself if needed.
Demo:https://github.com/zhaoya188/completed-load-webview
I've been visiting Stack Overflow for many years and it is the first time that I can't find any post that can solve my problem (at least I didn't see any).
I have a GridView with a custom adapter, which I have overridden to return a custom view made by an ImageView and a TextView.
I load the images after JSON parsing them from URLs with an AsyncTask, storing all the info into an ArrayList in the doInBackground() method and calling notifyDataSetChanged() in the onPostExecute() method. Everything's fine.
Now my problem is that when I launch the activity it takes a time of 5-10 seconds before the grid view will create and present itself to the user in entity. I'm wondering if there is a way to show the grid view with the text info first and then each image will load. Is this possible or not because they are both created in the same method?
#Override
public View getView(int arg0, View arg1, ViewGroup arg2) {
View v = null;
if (arg1 == null) {
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
v = inflater.inflate(R.layout.custom_product_view, null);
} else {
v = arg1;
}
iv = (ImageView) v.findViewById(R.id.product_image);
imageLoader.DisplayImage(products.get(arg0).getImage(), iv);
TextView tv = (TextView) v.findViewById(R.id.product_price);
tv.setText(products.get(arg0).getPrice());
return v;
}
I also must inform you as you can see from the DisplayImage() method that I have implemented this lazy loading: Lazy load of images in ListView. It works fine but the thing is that it loads the whole view again. What I want to do is launch the activity, load caption first and then the image will load when it finishes downloading. With this code here, it just lazy loads the whole View that every cell of the grid view contains. I earned some seconds because I don't download all the images at once like before but still it's not what I'm searching for.
Thanks a lot.
Follow this approach.
First, create a custom WebImageView class as follows.
public class WebImageView extends ImageView {
private Drawable placeholder, image;
public WebImageView(Context context) {
super(context);
}
public WebImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public WebImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setPlaceholderImage(Drawable drawable) {
placeholder = drawable;
if (image == null) {
setImageDrawable(placeholder);
}
}
public void setPlaceholderImage(int resid) {
placeholder = getResources().getDrawable(resid);
if (image == null) {
setImageDrawable(placeholder);
}
}
public void setImageUrl(String url) {
DownloadTask task = new DownloadTask();
task.execute(url);
}
private class DownloadTask extends AsyncTask<String, Void, Bitmap> {
#Override
protected Bitmap doInBackground(String... params) {
String url = params[0];
try {
URLConnection conn = (new URL(url)).openConnection();
InputStream is = conn.getInputStream();
BufferedInputStream bis = new BufferedInputStream(is);
ByteArrayBuffer baf = new ByteArrayBuffer(50);
int current = 0;
while ((current=bis.read()) != -1) {
baf.append((byte)current);
}
byte[] imageData = baf.toByteArray();
return BitmapFactory.decodeByteArray(imageData, 0, imageData.length);
} catch (Exception e) {
return null;
}
}
#Override
protected void onPostExecute(Bitmap result) {
image = new BitmapDrawable(result);
if (image != null) {
setImageDrawable(image);
}
}
}
}
Next, in Activity use the above custom ImageView as follows:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
WebImageView imageView = (WebImageView) findViewById(R.id.webimage);
imageView.setPlaceholderImage(R.drawable.ic_launcher);
imageView.setImageUrl("http://www.google.co.in/images/srpr/logo3w.png");
}
In brief, you are setting a placeholder image for the ImageView which gets replaced by the actual image when download completes. So the GridView will render immediately without delay.
Implementation Details:
So in your custom view (with an image + text) instead of using a simple ImageView, use WebImageView as shown above. When you get the JSON response set the TextView with the caption and the WebImageView with the image url.
So the caption will display immediately and the Image will load lazily.
I have used the below class to implement the Lazy loading of the images it works awesome for me . You try it also.
ImageLoader
/**
* This is class for display image in lazy-loading way.
*/
public class ImageLoader
{
private static final String TAG = ImageLoader.class.getSimpleName();
private InputStream m_is = null;
private OutputStream m_os = null;
private Bitmap m_bitmap = null;
private String m_imagePath;
private File m_cacheDir;
private WeakHashMap<String, Bitmap> m_cache = new WeakHashMap<String, Bitmap>();
/**
* Makes the background thread low priority. This way it will not affect the
* UI performance.<br>
* Checks the Device SD card exits or not and assign path according this
* condition.
*
* #param p_context
* activity context
*/
public ImageLoader(Context p_context)
{
/**
* Make the background thread low priority. This way it will not affect
* the UI performance
*/
m_imageLoaderThread.setPriority(Thread.NORM_PRIORITY - 1);
/**
* Check the Device SD card exits or not and assign path according this
* condition.
*/
if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED))
{
m_imagePath = Environment.getExternalStorageDirectory() + "/Android/data/" + p_context.getPackageName();
m_cacheDir = new File(m_imagePath);
}
else
{
m_cacheDir = new File(p_context.getDir("Cache", Context.MODE_PRIVATE), "Cache");
}
if (!m_cacheDir.exists())
m_cacheDir.mkdirs();
}
/**
* Check Image exits on HashMap or not.If exist then set image to ImageView
* else send request in the queue.
*
* #param p_url
* image Url
* #param p_imageView
* image container
* #param p_prgBar
* progressbar that is displayed till image is not download from
* server.
*/
public void DisplayImage(String p_url, ImageView p_imageView, ProgressBar p_prgBar) throws CustomException
{
if (m_cache.containsKey(p_url))
{
p_prgBar.setVisibility(View.GONE);
p_imageView.setVisibility(View.VISIBLE);
p_imageView.setImageBitmap(m_cache.get(p_url));
}
else
{
queueImage(p_url, p_imageView, p_prgBar);
}
}
/**
* Clear old task from the queue and add new image downloading in the queue.
*
* #param p_url
* image Url
* #param p_imageView
* image container
* #param p_prgBar
* progressbar that is displayed till image is not download from
* server.
*/
private void queueImage(String p_url, ImageView p_imageView, ProgressBar p_prgBar) throws CustomException
{
try
{
m_imagesQueue.Clean(p_imageView);
ImageToLoad m_photoObj = new ImageToLoad(p_url, p_imageView, p_prgBar);
synchronized (m_imagesQueue.m_imagesToLoad)
{
m_imagesQueue.m_imagesToLoad.push(m_photoObj);
m_imagesQueue.m_imagesToLoad.notifyAll();
}
/**
* start thread if it's not started yet
*/
if (m_imageLoaderThread.getState() == Thread.State.NEW)
m_imageLoaderThread.start();
}
catch (CustomException c)
{
throw c;
}
catch (Throwable t)
{
CustomLogHandler.printErrorlog(t);
throw new CustomException(TAG + " Error in queueImage(String p_url, ImageView p_imageView, ProgressBar p_prgBar) of ImageLoader", t);
}
}
/**
* Checks in SD card for cached file.If bitmap is not available then will
* download it from Url.
*
* #param p_url
* imgae Url
* #return bitmap from Cache or from server.
*/
private Bitmap getBitmap(String p_url) throws CustomException
{
System.gc();
String m_fileName = String.valueOf(p_url.hashCode());
File m_file = new File(m_cacheDir, m_fileName);
// from SD cache
m_bitmap = decodeFile(m_file);
if (m_bitmap != null)
return m_bitmap;
// from web
try
{
Bitmap m_bitmap = null;
int m_connectionCode = 0;
m_connectionCode = HttpConnection.getHttpUrlConnection(p_url).getResponseCode();
if (m_connectionCode == HttpURLConnection.HTTP_OK)
{
m_is = new URL(p_url).openStream();
m_os = new FileOutputStream(m_file);
FileIO.copyStream(m_is, m_os);
m_os.close();
m_os = null;
m_bitmap = decodeFile(m_file);
m_is.close();
m_is = null;
HttpConnection.getHttpUrlConnection(p_url).disconnect();
}
return m_bitmap;
}
catch (CustomException c)
{
throw c;
}
catch (Throwable t)
{
CustomLogHandler.printErrorlog(t);
throw new CustomException(TAG + " Error in getBitmap(String p_url) of ImageLoader", t);
}
}
/**
* Decodes the Image file to bitmap.
*
* #param p_file
* Image file object
* #return decoded bitmap
*/
private Bitmap decodeFile(File p_file) throws CustomException
{
try
{
// decode image size
Bitmap m_retBmp = null;
System.gc();
int m_scale = 1;
if (p_file.length() > 400000)
{
m_scale = 4;
}
else if (p_file.length() > 100000 && p_file.length() < 400000)
{
m_scale = 3;
}
// decode with inSampleSize
if (p_file.exists())
{
BitmapFactory.Options m_o2 = new BitmapFactory.Options();
m_o2.inSampleSize = m_scale;
m_retBmp = BitmapFactory.decodeFile(p_file.getPath(), m_o2);
}
return m_retBmp;
}
catch (Throwable t)
{
CustomLogHandler.printErrorlog(t);
throw new CustomException(TAG + " Error in decodeFile(File p_file) of ImageLoader", t);
}
}
/**
* Stores image information
*/
private class ImageToLoad
{
public String m_url;
public ImageView m_imageView;
public ProgressBar m_prgBar;
public ImageToLoad(String p_str, ImageView p_img, ProgressBar p_prgBar)
{
m_url = p_str;
m_imageView = p_img;
m_imageView.setTag(p_str);
m_prgBar = p_prgBar;
}
}
ImagesQueue m_imagesQueue = new ImagesQueue();
/**
* This is method to stop current running thread.
*/
public void stopThread()
{
m_imageLoaderThread.interrupt();
}
/**
* Stores list of image to be downloaded in stack.
*/
class ImagesQueue
{
private Stack<ImageToLoad> m_imagesToLoad = new Stack<ImageToLoad>();
/**
* Removes all instances of this ImageView
*
* #param p_ivImage
* imageView
*/
public void Clean(ImageView p_ivImage) throws CustomException
{
try
{
for (int m_i = 0; m_i < m_imagesToLoad.size();)
{
if (m_imagesToLoad.get(m_i).m_imageView == p_ivImage)
m_imagesToLoad.remove(m_i);
else
m_i++;
}
}
catch (Throwable t)
{
CustomLogHandler.printErrorlog(t);
throw new CustomException(TAG + " Error in Clean(ImageView p_image) of ImageLoader", t);
}
}
}
/**
*
* This is class waits until there are any images to load in the queue.
*/
class ImagesLoader extends Thread
{
public void run()
{
try
{
while (true)
{
if (m_imagesQueue.m_imagesToLoad.size() == 0)
synchronized (m_imagesQueue.m_imagesToLoad)
{
m_imagesQueue.m_imagesToLoad.wait();
}
if (m_imagesQueue.m_imagesToLoad.size() != 0)
{
ImageToLoad m_imageToLoadObj;
synchronized (m_imagesQueue.m_imagesToLoad)
{
m_imageToLoadObj = m_imagesQueue.m_imagesToLoad.pop();
}
Bitmap m_bmp = getBitmap(m_imageToLoadObj.m_url);
m_cache.put(m_imageToLoadObj.m_url, m_bmp);
if (((String) m_imageToLoadObj.m_imageView.getTag()).equals(m_imageToLoadObj.m_url))
{
BitmapDisplayer m_bmpdisplayer = new BitmapDisplayer(m_bmp, m_imageToLoadObj.m_imageView, m_imageToLoadObj.m_prgBar);
Activity m_activity = (Activity) m_imageToLoadObj.m_imageView.getContext();
m_activity.runOnUiThread(m_bmpdisplayer);
}
}
if (Thread.interrupted())
break;
}
}
catch (InterruptedException e)
{
/*
* allow thread to exit
*/
}
catch (Throwable t)
{
CustomLogHandler.printErrorlog(t);
}
}
}
ImagesLoader m_imageLoaderThread = new ImagesLoader();
/**
* This class Used to display bitmap in the UI thread
*/
class BitmapDisplayer implements Runnable
{
Bitmap m_bmp;
ImageView m_imageView;
ProgressBar m_prgBar;
public BitmapDisplayer(Bitmap p_bmp, ImageView p_imgview, ProgressBar p_prgBar)
{
m_bmp = p_bmp;
m_imageView = p_imgview;
m_prgBar = p_prgBar;
}
public void run()
{
if (m_bmp != null)
{
m_imageView.setImageBitmap(m_bmp);
m_prgBar.setVisibility(View.GONE);
m_imageView.setVisibility(View.VISIBLE);
}
}
}
}
Use the above class as below:
First you need to put the ProgressBar in your custom layout where you have your ImageView as below:
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/RelativeImagelayout">
<ProgressBar android:id="#+id/Progress"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_marginTop="10dp"/>
<ImageView
android:id="#+id/ivImage"
android:layout_width="80dp"
android:layout_height="90dp"
android:layout_marginTop="10dp"
android:clickable="false"/>
</RelativeLayout>
In your adapter class create the instance of the ImageLoader class and use it as below in your getView method:
ImageView m_ibImage = (ImageView) v.findViewById(R.id.ivImage);
ProgressBar m_pbProgress = (ProgressBar) v.findViewById(R.id.Progress);
if (products.get(arg0).getImage().toString().equals(null)
|| products.get(arg0).getImage().toString().equals(""))
{
m_pbProgress.setVisibility(View.INVISIBLE);
m_ibImage.setVisibility(View.VISIBLE);
}
else if (!products.get(arg0).getImage().toString().equals(null))
{
m_imgLoader.DisplayImage(products.get(arg0).getImage(), m_ibImage,
m_pbProgress);
}
I hope it will help you.
Thanks
The answer you mentioned of is not good, in my opinion. For example if you have 50 images, when the user scrolls up/ down the entire list, that sample project will spawn 50 threads. That is bad for mobile devices like a cell phone. A side note, his concept "lazy list" is different to the one that Android SDK defines. For a sample code of lazy loading list view, have a look at:
[Android SDK]/samples/android-x/ApiDemos/src/com/example/android/apis/view/List13.java
where x is API level. You can test the compiled app in any emulators, open the app API Demos > Views > Lists > 13. Slow Adapter.
About your current approach. You shouldn't use AsyncTask to download images. The documentation says:
AsyncTasks should ideally be used for short operations (a few seconds at the most.)
You should instead:
Use a service to download images in background. Note that services run on main UI thread, so to avoid of NetworkOnMainThreadException, you need something like Thread in your service.
Use a content provider to manage the downloaded images on SD card. For instance you keep the map of original URLs to corresponding files downloaded.
Along with the content provider, use a CursorAdapter for your grid view, and loaders for your activity/ fragment which hosts the grid view.
Basically, in the first time the user opens your activity, you create new adapter and set it to the grid view. So it has a connection with the content provider. Then you start the service to check and download the images. For every image downloaded, you insert it into the content provider. The provider notifies any observers about changes ― your activity/ fragment (the loader) receives the notification and updates UI.
enter code hereI want to do an advertisement program in android from openx server. But i am getting a bigger advertisement in limited area . I want a advertisement to fit an specific area in all android phones of all android phone dimensions.
I tried this code::
In the Xml , i gave ::
<LinearLayout
android:layout_alignParentBottom="true"
android:background="#color/black"
android:gravity="center_horizontal|center_vertical"
android:id="#+id/lLayout_ua_Ads"
android:layout_height="38dip"
android:layout_gravity="center_horizontal|center_vertical"
android:layout_width="300dip"
android:orientation="vertical">
<xstream.android.loyalone.openxads.OpenxAdView
android:id="#+id/openAdView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</LinearLayout>
In the main program,i gave ::
class AdManager{
Context mContext;
LinearLayout mLinearLayoutAds;
Timer mReloadAdTimer;
OpenxAdView openAd;
public AdManager(Context context, LinearLayout linearLayoutAds){
this.mContext = context;
this.mLinearLayoutAds = linearLayoutAds;
mReloadAdTimer = new Timer();
openAd = (OpenxAdView)findViewById(R.id.openAdView);
openAd.setDeliveryURL("openx.marzar.net/www/delivery");
openAd.setZoneID(1);
}
private void startShowing(){
openAd.load();
mReloadAdTimer.schedule(timerTaskAdUpdate, 10000, 10000);
}
TimerTask timerTaskAdUpdate = new TimerTask() {
#Override
public void run() {
openAd.load();
}
};
protected void removeImage() {
// TODO Auto-generated method stub
this.mLinearLayoutAds.removeAllViews();
}
private void setAdWidjet(){
}
public void onUpdating() {
setAdWidjet();
}
protected void cleanUp() {
try{
mReloadAdTimer.cancel();
}catch (Exception e) {
}
}
}
For getting openx ad , i gave ::
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Random;
import android.content.Context;
import android.content.res.Resources;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.ViewGroup;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
public class OpenxAdView extends ViewGroup {
private static final String ATTRS_NS = "http://denivip.ru/schemas/android/openx/0.1";
private static final String LOGTAG = "OpenXAd";
private static final String PARAMETER_DELIVERY_URL = "delivery_url";
private static final String PARAMETER_JS_TAG_URL = "js_tag_url";
private static final String PARAMETER_ZONE_ID = "zone_id";
private static final String PARAMETER_HAS_HTTPS = "has_https";
private static final String PARAMETER_SOURCE = "source";
private static final String HTML_DOCUMENT_TEMPLATE = "<html><head><style>* {padding: 0; margin: 0; background-color: transparent;}</style></head>\n"
+ "<body>%s</pre></body></html>";
private static final String JS_TAG = ""
+ "<script type='text/javascript' src='%1$s?zoneid=%2$d&charset=UTF-8"
+ "&cb=%4$d&charset=UTF-8&source=%3$s'></script>";
private WebView webView;
private String deliveryURL;
private String jsTagURL = "ajs.php";
//private String jsTagURL = "avw.php";
private Integer zoneID;
private boolean hasHTTPS = false;
private String source;
private Random prng = new Random();
private Resources res;
/**
* Initialize widget.
*
* #param context
*/
public OpenxAdView(Context context) {
super(context);
this.res = context.getResources();
this.webView = new WebView(context);
initWebView();
}
/**
* Initialize widget. If delivery_url and zone_id attributes were set in
* layout file, ad will be loaded automatically.
*
* #param context
* #param attrs
* #param defStyle
*/
public OpenxAdView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.res = context.getResources();
initAttributes(attrs);
this.webView = new WebView(context, attrs, defStyle);
initWebView();
}
/**
* Initialize widget. If delivery_url and zone_id attributes were set in
* layout file, ad will be loaded automatically.
*
* #param context
* #param attrs
*/
public OpenxAdView(Context context, AttributeSet attrs) {
super(context, attrs);
this.res = context.getResources();
initAttributes(attrs);
this.webView = new WebView(context, attrs);
initWebView();
}
private void initAttributes(AttributeSet attrs) {
setDeliveryURL(attrs);
setJsTagURL(attrs);
setZoneID(attrs);
setHasHTTPS(attrs);
setSource(attrs);
}
private void initWebView() {
DisplayMetrics d=this.res.getDisplayMetrics();
int wndWidth= d.widthPixels;
int wndHeight= d.heightPixels;
if(wndHeight<=480)
webView.setInitialScale(42);
else
webView.setInitialScale(83);
WebSettings settings = webView.getSettings();
settings.setJavaScriptEnabled(true);
settings.setPluginsEnabled(true);
settings.setAllowFileAccess(false);
// settings.setPluginState(WebSettings.PluginState.ON);
webView.setBackgroundColor(0x00000000); // transparent
webView.setVerticalScrollBarEnabled(false);
webView.setHorizontalScrollBarEnabled(false);
webView.setWebChromeClient(new WebChromeClient());
addView(webView);
}
protected String getZoneTemplate(int zoneID) {
String raw;
try {
String zoneTag = String.format(JS_TAG,
(hasHTTPS ? "https://" : "http://") + deliveryURL + '/' + jsTagURL,
zoneID,
source == null ? "" : URLEncoder.encode(source, "utf-8"),
prng.nextLong());
raw = String.format(HTML_DOCUMENT_TEMPLATE, zoneTag);
return raw;
}
catch (UnsupportedEncodingException e) {
// Log.wtf(LOGTAG, "UTF-8 not supported?!", e);
}
return null;
}
#Override
protected void onLayout(boolean changed, int left, int top, int right,
int bottom) {
webView.layout(left, top, right, bottom);
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
load();
}
/**
* Load ad from OpenX server using the parameters that were set previously.
* This will not work if the following minimum required parameters were not
* set: delivery_url and zone_id.
*/
public void load() {
//webView.clearView();
if (zoneID != null) {
load(zoneID);
}
else {
Log.w(LOGTAG, "zoneID is empty");
}
}
/**
* Load ad from OpenX server using the parameters that were set previously
* and the supplied zoneID. This will not work if the required parameter
* delivery_url was not set.
*
* #see #load()
* #param zoneID ID of OpenX zone to load ads from.
*/
public void load(int zoneID) {
// check required parameters
if (deliveryURL != null) {
// webView.clearView();
webView.loadDataWithBaseURL(null, getZoneTemplate(zoneID), "text/html", "utf-8", null);
}
else {
Log.w(LOGTAG, "deliveryURL is empty");
}
}
public String getDeliveryURL() {
return deliveryURL;
}
/**
* The path to server and directory containing OpenX delivery scripts in the
* form servername/path. This parameter is required. Example:
* openx.example.com/delivery.
*
* #param deliveryURL
*/
public void setDeliveryURL(String deliveryURL) {
this.deliveryURL = deliveryURL;
}
private void setDeliveryURL(AttributeSet attrs) {
int delivery_url = attrs.getAttributeResourceValue(ATTRS_NS, PARAMETER_DELIVERY_URL, -1);
if (delivery_url != -1) {
this.deliveryURL = res.getString(delivery_url);
}
else {
this.deliveryURL = attrs.getAttributeValue(ATTRS_NS, PARAMETER_DELIVERY_URL);
}
}
public String getJsTagURL() {
return jsTagURL;
}
/**
* The name of OpenX script that serves ad code for simple JavaScript type
* tag. Default: ajs.php. This parameter usually does not need to be
* changed.
*
* #param jsTagURL
*/
public void setJsTagURL(String jsTagURL) {
this.jsTagURL = jsTagURL;
}
private void setJsTagURL(AttributeSet attrs) {
int js_tag_url_id = attrs.getAttributeResourceValue(ATTRS_NS, PARAMETER_JS_TAG_URL, -1);
if (js_tag_url_id != -1) {
this.jsTagURL = res.getString(js_tag_url_id);
}
else {
String js_tag_url = attrs.getAttributeValue(ATTRS_NS, PARAMETER_JS_TAG_URL);
if (js_tag_url != null) {
this.jsTagURL = js_tag_url;
}
}
}
public Integer getZoneID() {
return zoneID;
}
/**
* The ID of OpenX zone from which ads should be selected to display inside
* the widget. This parameter is required unless you use load(int) method.
*
* #param zoneID
*/
public void setZoneID(Integer zoneID) {
this.zoneID = zoneID;
}
private void setZoneID(AttributeSet attrs) {
int zone_id_rs = attrs.getAttributeResourceValue(ATTRS_NS, PARAMETER_ZONE_ID, -1);
if (zone_id_rs != -1) {
this.zoneID = new Integer(res.getInteger(zone_id_rs));
}
else {
int zone_id = attrs.getAttributeIntValue(ATTRS_NS, PARAMETER_ZONE_ID, -1);
if (zone_id != -1) {
this.zoneID = new Integer(zone_id);
}
}
}
public boolean hasHTTPS() {
return hasHTTPS;
}
/**
* Set this to true if ads should be served over HTTPS protocol. Default:
* false.
*
* #param hasHTTPS
*/
public void setHasHTTPS(boolean hasHTTPS) {
this.hasHTTPS = hasHTTPS;
}
private void setHasHTTPS(AttributeSet attrs) {
int has_https = attrs.getAttributeResourceValue(ATTRS_NS, PARAMETER_HAS_HTTPS, -1);
if (has_https != -1) {
this.hasHTTPS = res.getBoolean(has_https);
}
else {
this.hasHTTPS = attrs.getAttributeBooleanValue(ATTRS_NS, PARAMETER_HAS_HTTPS, false);
}
}
public String getSource() {
return source;
}
/**
* This parameter can be used to target ads by its value. It is optional.
*
* #param source
*/
public void setSource(String source) {
this.source = source;
}
private void setSource(AttributeSet attrs) {
int source_id = attrs.getAttributeResourceValue(ATTRS_NS, PARAMETER_SOURCE, -1);
if (source_id != -1) {
this.source = res.getString(source_id);
}
else {
this.source = attrs.getAttributeValue(ATTRS_NS, PARAMETER_SOURCE);
}
}
}
please give your solutions for this problem . Thanks in advance.
so I made an app which communicates with JSON on the web. It fetches images and texts from the JSON.
And now I got a task to make this app accessible without an internet connection.
it should be like this:
The first time it's launched, the app has to check if there's any internet connection or not. If not, pop up a dialog box 'Please check your internet connection'. If there's any, the app is opened and it has to download the images and texts from the JSON and save them into an external storage
The next time when the app is opened, when there's no internet it will load the images and text files from the external storage. And each time it's connected to the internet, it will download the files and replace the previous files.
Can anybody provide me a solution by modifying these classes below maybe?
public class ImageThreadLoader {
private static final String TAG = "ImageThreadLoader";
// Global cache of images.
// Using SoftReference to allow garbage collector to clean cache if needed
private final HashMap<String, SoftReference<Bitmap>> Cache = new HashMap<String, SoftReference<Bitmap>>();
private final class QueueItem {
public URL url;
public ImageLoadedListener listener;
}
private final ArrayList<QueueItem> Queue = new ArrayList<QueueItem>();
private final Handler handler = new Handler(); // Assumes that this is started from the main (UI) thread
private Thread thread;
private QueueRunner runner = new QueueRunner();;
/** Creates a new instance of the ImageThreadLoader */
public ImageThreadLoader() {
thread = new Thread(runner);
}
/**
* Defines an interface for a callback that will handle
* responses from the thread loader when an image is done
* being loaded.
*/
public interface ImageLoadedListener {
public void imageLoaded(Bitmap imageBitmap );
}
/**
* Provides a Runnable class to handle loading
* the image from the URL and settings the
* ImageView on the UI thread.
*/
private class QueueRunner implements Runnable {
public void run() {
synchronized(this) {
while(Queue.size() > 0) {
final QueueItem item = Queue.remove(0);
// If in the cache, return that copy and be done
if( Cache.containsKey(item.url.toString()) && Cache.get(item.url.toString()) != null) {
// Use a handler to get back onto the UI thread for the update
handler.post(new Runnable() {
public void run() {
if( item.listener != null ) {
// NB: There's a potential race condition here where the cache item could get
// garbage collected between when we post the runnable and it's executed.
// Ideally we would re-run the network load or something.
SoftReference<Bitmap> ref = Cache.get(item.url.toString());
if( ref != null ) {
item.listener.imageLoaded(ref.get());
}
}
}
});
} else {
final Bitmap bmp = readBitmapFromNetwork(item.url);
if( bmp != null ) {
Cache.put(item.url.toString(), new SoftReference<Bitmap>(bmp));
// Use a handler to get back onto the UI thread for the update
handler.post(new Runnable() {
public void run() {
if( item.listener != null ) {
item.listener.imageLoaded(bmp);
}
}
});
}
}
}
}
}
}
/**
* Queues up a URI to load an image from for a given image view.
*
* #param uri The URI source of the image
* #param callback The listener class to call when the image is loaded
* #throws MalformedURLException If the provided uri cannot be parsed
* #return A Bitmap image if the image is in the cache, else null.
*/
public Bitmap loadImage( final String uri, final ImageLoadedListener listener) throws MalformedURLException {
// If it's in the cache, just get it and quit it
if( Cache.containsKey(uri)) {
SoftReference<Bitmap> ref = Cache.get(uri);
if( ref != null ) {
return ref.get();
}
}
QueueItem item = new QueueItem();
item.url = new URL(uri);
item.listener = listener;
Queue.add(item);
// start the thread if needed
if( thread.getState() == State.NEW) {
thread.start();
} else if( thread.getState() == State.TERMINATED) {
thread = new Thread(runner);
thread.start();
}
return null;
}
/**
* Convenience method to retrieve a bitmap image from
* a URL over the network. The built-in methods do
* not seem to work, as they return a FileNotFound
* exception.
*
* Note that this does not perform any threading --
* it blocks the call while retrieving the data.
*
* #param url The URL to read the bitmap from.
* #return A Bitmap image or null if an error occurs.
*/
public static Bitmap readBitmapFromNetwork( URL url ) {
InputStream is = null;
BufferedInputStream bis = null;
Bitmap bmp = null;
try {
URLConnection conn = url.openConnection();
conn.connect();
is = conn.getInputStream();
bis = new BufferedInputStream(is);
bmp = BitmapFactory.decodeStream(bis);
} catch (MalformedURLException e) {
Log.e(TAG, "Bad ad URL", e);
} catch (IOException e) {
Log.e(TAG, "Could not get remote ad image", e);
} finally {
try {
if( is != null )
is.close();
if( bis != null )
bis.close();
} catch (IOException e) {
Log.w(TAG, "Error closing stream.");
}
}
return bmp;
}
}
and
public class ProjectAdapter extends ArrayAdapter<Project> {
int resource;
String response;
Context context;
private final static String TAG = "MediaItemAdapter";
private ImageThreadLoader imageLoader = new ImageThreadLoader();
//Initialize adapter
public ProjectAdapter(Context context, int resource, List<Project> items) {
super(context, resource, items);
this.resource=resource;
}
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
TextView textTitle;
final ImageView image;
Project pro = getItem(position);
LinearLayout projectView;
//Inflate the view
if(convertView==null)
{
projectView = new LinearLayout(getContext());
String inflater = Context.LAYOUT_INFLATER_SERVICE;
LayoutInflater vi;
vi = (LayoutInflater)getContext().getSystemService(inflater);
vi.inflate(resource, projectView, true);
}
else
{
projectView = (LinearLayout) convertView;
}
try {
textTitle = (TextView)projectView.findViewById(R.id.txt_title);
image = (ImageView)projectView.findViewById(R.id.image);
} catch( ClassCastException e ) {
Log.e(TAG, "Your layout must provide an image and a text view with ID's icon and text.", e);
throw e;
}
Bitmap cachedImage = null;
try {
cachedImage = imageLoader.loadImage(pro.smallImageUrl, new ImageLoadedListener() {
public void imageLoaded(Bitmap imageBitmap) {
image.setImageBitmap(imageBitmap);
notifyDataSetChanged(); }
});
} catch (MalformedURLException e) {
Log.e(TAG, "Bad remote image URL: " + pro.smallImageUrl, e);
}
textTitle.setText(pro.project_title);
if( cachedImage != null ) {
image.setImageBitmap(cachedImage);
}
return projectView;
}
}
Thank you!
Create a database with the names and paths of the downloaded images. Upon onCreate() (or wherever you want to do the check), read the database and check if it's empty or not. If not, then use the images.