I'm attempting to display an HTML string in a WebView named webDescription. Because this HTML can sometimes be lengthy, I want to limit the height of the WebView to a maximum dimension and allow the content to scroll within the view.
I am using the following code to wait until the page finishes loading, check the content height and set the height of the enclosing (parent) TableRow to a maximum of 150.
webDescription.setWebViewClient(new WebViewClient() {
public void onPageFinished(WebView view, String url) {
// do your stuff here
Log.i("EVENTDETAIL", "Web view has finished loading");
Log.i("EVENTDETAIL", "Description content height: " + view.getContentHeight());
if ( view.getContentHeight() > 150 ) {
LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, 150);
view.setLayoutParams(params);
}
}
});
However, view.getContentHeight() always returns 0; thus, the LayoutParams of the parent TableRow never get changed.
Can anyone offer any insight into this and how I might fix my problem? Thanks so much for your consideration.
use this code:
if(view.getHeight()>150)
{
LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, 150);
view.setLayoutParams(params);
}
I meant use getHeight() instead of getContentHeight()
I would set the WebView to match_parent and set a maxHeight on the TableRow.
Related
My purpose is to have an invisible LinearLayout that appear whit an animation when click on a specific button. To do this i have set the default height to WRAP_CONTENT, get the height when the app start, set the height to 0 and start the animation when i click on the button. Here is the code:
linearLayout.post(new Runnable() {
#Override
public void run(){
height = linearLayout.getMeasuredHeight();
linearLayout.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, 0));
}
});
findViewById(R.id.btnOperator).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Animation ani = new ShowAnim(linearLayout, height/* target layout height */);
ani.setDuration(1000/* animation time */);
linearLayout.startAnimation(ani);
}
});
This work pretty good, but i want to do different. I want that the default height is 0, and then calculate what the WRAP_CONTENT height would be, and pass it to:
Animation ani = new ShowAnim(linearLayout, height/* target layout height */);
How could i do this? I searched but found anything.
Try this code:
linearLayout.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
height = linearLayout.getMeasuredHeight();
Currently, I'm trying to make an infinitely long chain of ImageViews that are all next to each other.
I attempt to do this by using addRule and LayoutParam so that every new ImageView has to be set to the right of the ImageView before it.
However, I encounter an odd problem. When I do run my code, this is the result :
Not only are my two ImageViews not even next to each other, it doesn't work for any number past two! If I just create two blocks, it gives the result below.
If I make any more blocks than 2, they'll just stack on top of block #2. Which is what happens in the image above.
Also, I have to set every ImageView with its own ID because if I don't, they'll all have an ID of -1.
I've been stuck on this for like 5 hours in a row, and it's really starting to get annoying. (Please do not suggest that I use a LinearLayout. I already know, and I can't use it.)
public class TerrainFactory {
int count = 0;
int idCount = 1;
Terrain terrain;
public Terrain createNewTerrain(Activity activity, RelativeLayout relativeLayout,
final ArrayList<Terrain> terrainArrayList){
//TODO: Add the rectangle bounds into this.
terrain = new Terrain();
terrain.is = activity.getResources().openRawResource(+R.drawable.game_tile);
terrain.tile = BitmapFactory.decodeStream(terrain.is);
terrain.tileDrawable = new BitmapDrawable(activity.getResources(), terrain.tile);
terrain.terrainImage = new ImageView(activity);
terrain.terrainImage.setImageDrawable(terrain.tileDrawable);
//noinspection ResourceType
terrain.terrainImage.setId(idCount);
idCount++;
if(count >= 1) {
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);;
layoutParams.addRule(RelativeLayout.RIGHT_OF, terrainArrayList.get(count - 1).terrainImage.getId());
relativeLayout.addView(terrain.terrainImage, layoutParams);
terrain.terrainImage.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
public void onGlobalLayout() {
//TODO: Set rectangle bounds in here
++count;
terrain.terrainImage.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
});
}else{
++count;
relativeLayout.addView(terrain.terrainImage);
terrain.terrainImage.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
public void onGlobalLayout() {
//TODO: Set rectangle bounds in here
terrain.terrainImage.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
});
}
return terrain;
}
public int getCount() { return count; }
}
Here is my PlayActivity class, where all the block creating happens :
public class PlayActivity extends Activity {
RelativeLayout relativeLayout;
TerrainFactory terrainFactory;
ArrayList<Terrain> terrainArrayList = new ArrayList<Terrain>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_play);
relativeLayout = (RelativeLayout) findViewById(R.id.relativeLayout);
terrainFactory = new TerrainFactory();
for(int i = 0; i < 7; i++){
terrainArrayList.add(terrainFactory.createNewTerrain(PlayActivity.this, relativeLayout, terrainArrayList));
}
}
}
And here is the Terrain class
public class Terrain {
public Bitmap tile;
public InputStream is;
public ObjectAnimator moveLeft;
public Drawable tileDrawable;
public Rect bounds;
public ImageView terrainImage;
}
Here's the XML for the RelativeLayout. The RelativeLayout I'm using is the one nested inside the RelativeLayout that fills the entire screen.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.hooray.project.fun.activity.PlayActivity">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="false"
android:id="#+id/relativeLayout"></RelativeLayout>
</RelativeLayout>
Basically I jsut would tackle this in another way....using not Views for buildiung terrains but using surfaceview and draw bitmaps on it...
But well lets do it your way:
Place your views into a FrameLayout. Then when you create your views give them their dimension in code and also set the x and y positions in code.
For example this is how I give a View the needed Dimension and the position in my Code. Its a Textview and I jsut set the x and y position which I have stuffed iinto variables so I can chnage them how i want:
tView.setLayoutParams(new FrameLayout.LayoutParams(hintListWidth,hintListHeight));
tView.setX(hintListPosX);
tView.setY(hintListPosY);
maybe for your example do it like this:
float startPosX=???;
float startPosY=???;
float i=0;
for(Terrian t:terrainArrayList)
{
tView.setLayoutParams(new FrameLayout.LayoutParams(width,height));
tView.setX(startPosX+width*i);
tView.setY(startPosY);
i++;
}
If you have the width and height ready and know where to start placing the first view every view will be placed side by side from left to right...
As suggested in the other answer(s) to your question, the problem is that counter on the if-true branch is never incremented before views are drawn (aka tree observer callback being called). You are calling addView but I think this goes on and adds the view asynchronously, while you go on and add views. So you finish adding the views and only after that they are drawn. I suggest taking the counter out of the callback and place it at the end of the if-true block.
Note: if you are targeting SDK17+ you can use View.generateViewId() instead of manually keeping track / incrementing it yourself. Not that it matters in this particular scenario, but it will not allow the generated id to conflict with aapt-generated ids and it will roll it over once the top limit is reached.
I use NineOldAndroids library to scale my custom layout.
public class MyLayout extends FrameLayout {
// LayoutParams.MATCH_PARENT and all.
...
#Override
public boolean setPositionAndScale(ViewGroup v, PositionAndScale pas, PointInfo pi) {
...
mScale = pas.getScale();
ViewHelper.setScaleX(this, mScale);
ViewHelper.setScaleY(this, mScale);
}
}
I have tried FrameLayout and AbsoluteLayout. All have the same effect.
When mScale < 1.0 scaling/zooming works but part of the layout is clipped.
mScale = 1.0:
mScale < 1.0: scaling/zooming works but layout is clipped
How can i fix this issue?
Edit: The picture was taken on ICS. So I don't think it's NineOldAndroids problem.
The parent of your view must have the property android:clipChildren disabled (from layout file or with setClipChildren(false) ).
But with this method you don't get the touch events outside the view clip bounds. You can work around by sending them from your activity or writing a custom ViewGroup parent.
I'm using a different hack which seems to work in my case, the trick is to maintain your own transformation matrix. Then, you have to overload a lot of ViewGroup's method to make it work. For example :
#Override
protected void dispatchDraw(Canvas canvas) {
Log.d(TAG, "dispatchDraw " + canvas);
canvas.save();
canvas.concat(mMatrix);
super.dispatchDraw(canvas);
canvas.restore();
}
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.d(TAG, "dispatchTouchEvent " + ev);
ev.transform(getInvMatrix()); //
return super.dispatchTouchEvent(ev);
}
private Matrix getInvMatrix()
{
if(!mTmpMatIsInvMat)
mMatrix.invert(mTmpMat);
mTmpMatIsInvMat = true;
return mTmpMat;
}
In case anyone got in to the same situation as me.
I ended up using this approach:
protected void setScale(float scale, boolean updateView) {
mScale = scale;
if (updateView) {
LayoutParams params = getLayoutParams();
onUpdateScale(scale, params);
setLayoutParams(params);
}
}
protected void onUpdateScale(float scale, LayoutParams params) {
params.leftMargin = (int) (mModel.getX() * scale);
params.topMargin = (int) (mModel.getY() * scale);
params.width = (int) (mModel.getWidth() * scale);
params.height = (int) (mModel.getHeight() * scale);
}
Since API Level 11, the View class has setScaleX() and setScaleY() methods, that work as expected and also scale sub-views of the scaled view. So, if that'd be a way for you, drop the library and just do
v.setScaleX(mScale);
v.setScaleY(mScale);
If I understand your problem correctly, you are scaling a view group and expect the included views to scale accordingly. It doesn't work that way: you scale the view group and it changes size, but its children views do not.
Just scale all subviews. Even so, I am not sure that texts and images are going to be automatically scaled. What you want is zoom, not scale. Try this reference.
Use ViewGroup.layout. It may be the easiest way to scale(&move) ViewGroup.
I have a LinearLayout, and this LinearLayout will hold dynamically placed views. I need to find out what the width of the children of LinearLayout, however this has to be done in onCreate method. From researching I've found out that you can't use getWidth from this method. So instead I'm using onWindowFocusChanged, which works for the parent LinearLayout (returning me the actual size), but it doesn't work with its children.
Another thing I noticed is that when the screen is fading away and the screen is locked, I can see at the logs the actual width of the children being returned (I think the activity is being paused).
I'm really stuck and this is needed because I need to dynamically place those views depending on the children width.
You might be able to get with the below. But as others pointed out, this probably isn't a great idea.
LinearLayout.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
LinearLayout.getMeasuredWidth();
inside the onCreate , views still can't know the state of the nearby views and the children ,etc... so only after all is prepared and the layout process is done , you can get the size of views .
here's a quick code for getting the size of the view just before it's being drawn:
private static void runJustBeforeBeingDrawn(final View view, final Runnable runnable)
{
final ViewTreeObserver vto = view.getViewTreeObserver();
final OnPreDrawListener preDrawListener = new OnPreDrawListener()
{
#Override
public boolean onPreDraw()
{
Log.d(App.APPLICATION_TAG, CLASS_TAG + "onpredraw");
runnable.run();
final ViewTreeObserver vto = view.getViewTreeObserver();
vto.removeOnPreDrawListener(this);
return true;
}
};
vto.addOnPreDrawListener(preDrawListener);
}
alternatively , you can use addOnGlobalLayoutListener instead of addOnPreDrawListener if you wish.
example of usage :
runJustBeforeBeingDrawn(view,new Runnable()
{
#Override
public void run()
{
int width=view.getWidth();
int height=view.getHeight();
}
});
another approach is to use onWindowFocusChanged (and check that hasFocus==true) , but that's not always the best way ( only use for simple views-creation, not for dynamic creations)
EDIT: Alternative to runJustBeforeBeingDrawn: https://stackoverflow.com/a/28136027/878126
https://stackoverflow.com/a/3594216/1397218
So you should somehow change your logic.
I have loaded an image with WebView but when it is zoomed out max the image place itself in the top left corner of my device. Is there anyway to load the image so it is displayed in center?
Cheers!
I did it with a combination of xml and code. I have my gif file stored in assets folder.
Following is the xml layout code:
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent" >
<WebView
android:id="#+id/webView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true" />
</RelativeLayout>
Following is the java code:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_webview);
WebView webView = (WebView) findViewById(R.id.webView);
webView.setWebViewClient(new WebViewController());
webView.loadDataWithBaseURL("file:///android_asset/","<html><center><img src=\"animation.gif\"></html>","text/html","utf-8","");
}
private class WebViewController extends WebViewClient {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
}
You can use this code to center the image at the center of the screen in a WebView :
//first you will need to find the dimensions of the screen
float width;
float height;
float currentHeight;
WebView mWebView;
//this function will set the current height according to screen orientation
#Override
public void onConfigurationChanged(Configuration newConfig){
if(newConfig.equals(Configuration.ORIENTATION_LANDSCAPE)){
currentHeight=width;
loadImage();
}if(newConfig.equals(Configuration.ORIENTATION_PORTRAIT)){
currentHeight=height;
loadImage();
}
}
//call this function and it will place the image at the center
public void load and center the image on screen(){
mWebView=(WebView)findViewById(R.id.webview);
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.getSettings().setBuiltInZoomControls(true);
mWebView.setBackgroundColor(0);
DisplayMetrics displaymetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
height = displaymetrics.heightPixels;
width = displaymetrics.widthPixels;
currentHeight=height //assuming that the phone
//is held in portrait mode initially
loadImage();
}
public void loadImage(){
Bitmap BitmapOfMyImage=BitmapFactory.decodeResource(Environment.getExternalStorgeDirectory().getAbsolutePath()+"yourFolder/myImageName.jpg");
mWebView.loadDataWithBaseURL("file:///"+Environment.getExternalStorgeDirectory().getAbsolutePath()
+"yourFolder/","<html><center>
<img src=\"myImageName.jpg\" vspace="
+(currentHeight/2-(BitmapOfMyImage.getHeight()/2))+">
</html>","text/html","utf-8","");
//This loads the image at the center of thee screen
}
I have used the center tag to center the image vertically and then used the vspace tag to give image top margin . Now the margin is calculated by : screenVierticalHeight/2-ImageVerticalHeight/2
Hope this helps
mWebView.loadDataWithBaseURL("file:///"+Environment.getExternalStorgeDirectory().getAbsolutePath()+"yourFolder/","<html><center><img src=\"myImage.jpg\"></html>","text/html","utf-8","");
This places image from SDCard at center of the webView. I hope this helps
String html = "<html><head><style type='text/css'>html,body {margin: 0;padding: 0;width: 100%;height: 100%;}html {display: table;}body {display: table-cell;vertical-align: middle;text-align: center;}</style></head><body><p style=\"color:white\">+"you message"+</p></body></html>";
webView.loadData(html, "text/html; charset=UTF-8", null);
So this will load your webview with the text in center
Try to load an HTML file that embeds the image instead. All the layout may done with standard css styles then.
I had the same problem - centering vertically in webview. This solution works most of the time... maybe it can help you a little
int pheight = picture.getHeight();
int vheight = view.getHeight();
float ratio = (float) vheight / (float) pheight;
System.out.println("pheight: " + pheight + "px");
System.out.println("vheight: " + vheight + "px");
System.out.println("ratio: " + ratio + "px");
if (ratio > 0.5)
{
return;
}
int diff = pheight - vheight;
int diff2 = (int) ((diff * ratio) / 4);
if (pheight > vheight)
{
// scroll to middle of webview
currentPhotoWebview.scrollTo(0, diff2);
System.out.println("scrolling webview by " + diff2 + "px");
}
I know this answer's a little late but here's an option without javascript:
you can extend WebView and use the protected methods computeHorizontalScrollRange() and computeVerticalScrollRange() to get an idea of the maximum scroll range and based on that calculate where you want to scrollTo with computeHorizontalScrollRange()/2 - getWidth()/2 and similarly for vertical: computeVerticalScrollRange()/2 - getHeight()/2
my only advice would be to make sure that loading is complete before doing so, i haven't tested if this works while things are loading but i don't think it would as the webview won't yet have it's scroll range correctly set (as content is still being loaded)
see: Android webview : detect scroll where i got a lot of my info from.
For me that is work:
String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "PICTURE FILE NAME";
File file = new File(path);
String html = "<style>img{height: 100%;max-width: 100%;}</style> <html><head></head><body><center><img src=\"" + imagePath + "\"></center></body></html>";
webView.getSettings().setDefaultZoom(ZoomDensity.FAR);
webView.getSettings().setBuiltInZoomControls(true);
webView.getSettings().setUseWideViewPort(true);
webView.getSettings().setLoadWithOverviewMode(true);
webView.loadDataWithBaseURL( "" , html, "text/html", "UTF-8", "");