I have a WebView and I would like my app to find a mark on the text(I'm using the symbol ► in the html).I have lots of html files each with almost 10 of these markings.
I have done it pretty easily with TextView doing this:
int offset=texto.indexOf("SPECIFIC MARKING ON TEXT");
final int line = textview.getLayout().getLineForOffset(offset);
final int y = textview.getLayout().getLineTop(line); // e.g. I want to scroll to line
final ScrollView s = (ScrollView)findViewById(R.id.ScrollView01);
s.post(new Runnable() {
#Override
public void run() {
s.smoothScrollTo(0, y);
}
});
But how do I accomplish that on Webview ?
The reason I use Webview is due to better text formatting.
In fact, it's even simpler with a WebView. You can use the findAll method for the first occurrence, then findNext(true) for the following markers. The view will automatically scroll to the requested text.
Related
I have a WebView with android:layout_height="wrap_content" inside a ScrollView. Prior to Android 7 this resulted in the WebView resizing to the height of the local html content I set with loadData. On my Nexus 5X with Android 7 though, the WebView height seems to be unreliable, sometimes it only shows parts of the first text line, sometimes there's a big gap at the end of the content.
I think it could be due to Google now using Chrome for WebViews starting with Nougat.
Does anyone have an explanation or fix/workaround for this issue?
It might also be important that the views are contained in the cells of a RecyclerView .
Workaround is:
waiting while html page will be loaded and run JS inside page to detect content height and set it to WebView Layout Parameters height.
It is easy to run JS inside page, just navigate to url like
javascript:Math.max(document.body.scrollHeight,document.body.offsetHeight,document.documentElement.clientHeight,document.documentElement.scrollHeight,document.documentElement.offsetHeight);
To pass the result to Java code you must provide Java-Java Script interface as described here https://developer.android.com/guide/webapps/webview.html (Binding JavaScript code to Android code)
Your url to navigate must looks like
javascript:myfunc(Math.max(document.body.scrollHeight,document.body.offsetHeight,document.documentElement.clientHeight,document.documentElement.scrollHeight,document.documentElement.offsetHeight));
The myfunc will be called and you will get the height of page. Now just set in height to WebView height.
Using WebView to showing contents inside a RecyclerView is a little bit risky and it is not so efficient. You have no idea how it works in different devices!
I suggest to change your solution using TextView and converting HTML contents to formatted text using this command
textView.setText(Html.fromHtml(<YOUR HTML CONTENT>))
By the way, These are some efforts I have done with WebView to achieve a fix size for showing some ads banner content:
public class AdsWebView extends WebView {
// some setup codes...
private static final int MAX_SCALE = 120;
public AdsWebView(Context context) {
setVerticalScrollBarEnabled(false);
setHorizontalScrollBarEnabled(false);
setScrollbarFadingEnabled(false);
setupAdsScale();
}
private int getAdsScale() {
int width = getDisplayWidth(getContext());
Double val = Double.valueOf(width) / Double.valueOf(480);
val = val * 100d;
return val.intValue();
}
private void setupAdsScale() {
int scale = getAdsScale();
if (scale > MAX_SCALE)
scale = MAX_SCALE;
setInitialScale(scale);
}
private int getDisplayWidth(Context context) {
try {
Activity activity = (Activity) context;
if (Integer.valueOf(android.os.Build.VERSION.SDK_INT) < 13) {
Display display = activity.getWindowManager()
.getDefaultDisplay();
return display.getWidth();
} else {
Display display = activity.getWindowManager()
.getDefaultDisplay();
Point size = new Point();
display.getSize(size);
return size.x;
}
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
I have a form within a ScrollView. When I tap into an EditText the soft keyboard appears and the ScrollView scrolls the now focused EditText so that it just comes into view.
However, I have hint information just below the EditText that I also would like to show, so the scrolling should go just a bit further up, like this:
The EditText is embedded in a form element and actually I'd like to scroll to the bottom of that. I've checked the source code of ScrollView and it will just scroll to the bottom of the currently focused view. Maybe there's a way to tell the ScrollView that the form element is the currently focused element?
Of course I could write my own ScrollView sub class and override the scroll behavior, but I wonder if there's a more elegant way of doing this.
Any other suggestions (with adjust scrolling with a fixed offset or so) are also appreciated.
I have not really found any way to configure the scrolling behavior of the ScrollView from the outside. So I ended up to define my own sub class of ScrollView:`
/**
* {#link ScrollView} extension that allows to configure scroll offset.
*/
public class ConfigurableScrollView extends ScrollView {
private int scrollOffset = 0;
public ConfigurableScrollView (final Context context, final AttributeSet attrs) {
super(context, attrs);
}
public void setScrollOffset (final int scrollOffset) {
this.scrollOffset = scrollOffset;
}
#Override
protected int computeScrollDeltaToGetChildRectOnScreen (final Rect rect) {
// adjust by scroll offset
int scrollDelta = super.computeScrollDeltaToGetChildRectOnScreen(rect);
int newScrollDelta = (int) Math.signum(scrollDelta) * (scrollDelta + this.scrollOffset);
return newScrollDelta;
}
}
computeScrollDelta(...) is the only protected method that can be targeted for overriding, apart from onSizeChanged(...).
The signum function in the example above ensures that scrolling is only increased, if the ScrollView really thinks that scrolling is necessary (e.g. when keyboard pops up).
I can now set the extra scroll offset once from the outside, as calculated from the height of the hint.
It's not hard to use the extended ConfigurableScrollView instead of the standard ScrollView, I only had to replace the ScrollView XML tag with the FQN of the new class.
Considering that you are using ScrollView you have the possibility to use the method ScrollTo as follow:
scrollView.post(new Runnable() {
#Override
public void run() {
sv.scrollTo(x-value, y-value);
}
});
where the first argument is the scroll value for X, while the second argument is the scroll value for Y. So you just have to set your scrollView offset when the keyboard is displayed.
Hope it helps;)
Part of application I'm currently working on act exactly as android shell, shows several pages filled with icons with text labels. User could slide between pages to find needed element. I'm using PagerView with GridView in each page.
Content of each page should fit exactly of visible area, no scroll. The question how to calculate number of icons on each page?
The issue next, I can't call pagerView.getHeight(), I'll have 0 in result because actual layout calculation wasn't executed yet.
UPDATED:
Seems I wasn't able to describe my problem well, I'll try to provide more simple case, suppose I do have activity with status bar at the top and some button bar at the bottom, both fixed height. Whole remaining area in the middle is used by GridView.
Grid view should show rectangular icons, and what I need to calculate is how many icons it could show without scroll (because remaining icons will be shown on next activity).
You can try something like:
public int countElements(ViewGroup group) {
int count = 0;
for (int i = 0; i < group.getChildCount(); i++) {
View v = layout.getChildAt(i);
Class c = v.getClass();
if (c == *Icon*) {
count++;
}
}
return count;
}
In case you have inner views you can check if c is a GroupView and call the method recursively.
As for your second issue, try using this in onCreate:
private void calculateSize(int height, int width){
int rows = Math.floor(height/imageHeight);
int columns = Math.floor(width/imageWidth);
//do something with numbers
}
ViewTreeObserver vto = pagerView.getViewTreeObserver();
if(vto.isAlive()){
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
vto.removeGlobalOnLayoutListener(this);
calculateSize(gridView.getHeight(), gridView.getWidth());
}
});
}
I have a CordovaWebView that presents some html forms. When i focus on an input field, the Android's soft keyboard pops up and, for certain fields, according to their position, it gets on top of it. Basically, it is not resizing the layout of CordovaWebView.
Whatever i do, i can't change this, and it is said that it is related to the fact that the CordovaWebView is in fullscreen mode.
How can i accomplish to resolve this?
PS: is it, or not, a bug?
Thank you all!
In fact, it is a well know bug, as #user2493245 said. But I found a workaround, at least regarding my specific case.
on WebView, just check for the coordinates for the View's visible area.
final View activityRootView = this.root;
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
public void onGlobalLayout() {
Rect r = new Rect();
//r will be populated with the coordinates of your view that area still visible.
activityRootView.getWindowVisibleDisplayFrame(r);
int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
if(heightDiff != lastValue) {
if (heightDiff > 100) { // if more than 100 pixels, its probably a keyboard...
appView.sendJavascript("onKeyBoardShow(" + r.bottom + ");");
} else {
appView.sendJavascript("onKeyBoardHide();");
}
lastValue = heightDiff;
}
}
});
As you can see, I send that information to the WebView. On HTML, I have this two methods to handle the issue:
function onKeyBoardShow(bottom) {
var diff = ($('input[type=text]:focus').offset().top - bottom) + 50;
if(diff > 0) {
$('body').css("top", (diff * -1) + "px");
}
};
function onKeyBoardHide() {
$('body').css("top", "0px");
};
Basically, onKeyBoardShow, it gets the input field focused and calculates the amount of pixels that will be necessary to move the body, allowing the user to see the input field. onKeyBoardHide, simply puts the body to its original position.
PS: This only functions when the viewport targets devicedpi, as we need to modify the way we get the dif regarding the dpi of the device.
PS2: First amount of code is not mine, I only edited to fill my needs. I saw that on a SO question, but unfortunatelly now i can't find it. If i find it, i'll post the link here.
I have the same problem and i found out it is a well known bug.
A workaround could be that u write a plugin that disables the fullscreen just before the softkeyboard pops up and reenables it afterwards.
Remove android:theme="#android:style/Theme.NoTitleBar.Fullscreen" from manifest, and add these 2 lines of code:
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().addFlags(LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
Make sure your onCreate method looks like this:
#Override
protected void onCreate(Bundle savedInstanceState) {
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().addFlags(LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
super.onCreate(savedInstanceState);
setContentView(R.layout.yourLayout);
// Your code ...
}
And everything will work :D.
I'd appreciate it if someone could help me with my problem.
I am trying to change the image in an ImageView when someone clicks on it. I've put my images in an array and I am using a while loop to cycle once through all of them.
My problem is that while the first image (image8, not in the array) shows in the view all the other (after creating the OnClickListener) do not. Actually nothing happens and I am not sure where the mistake is. Thanks in advance.
This is the problematic code:
final int array[]=new int[5];
array[0]= R.drawable.image6;
array[1]= R.drawable.image4;
array[2]= R.drawable.image9;
array[3]= R.drawable.image4;
array[4]= R.drawable.image5;
ImageView touchView = (ImageView)findViewById(R.id.imageview);
touchView.setOnTouchListener(new View.OnTouchListener()
{
#Override
public boolean onTouch(View touchView, MotionEvent ev) {
//get coordinates of touch event
int x = (int)ev.getRawX();
int y = (int)ev.getRawY();
---Code missing---
((ImageView)touchView).setImageResource(R.drawable.image8);
touchView.setOnClickListener(new View.OnClickListener() {
int counter = 0;
#Override
//Image change on every click
public void onClick(View touchView) {
while(counter<5){
((ImageView) touchView).setImageResource(array[counter]);
counter++;
});
You can't change images in a sequence like that and expect anything to show on the screen. You should use the click to start a separate thread that will do the image animation. See the description of the android.view.animation package. It sounds like the AnimationDrawable class will give you exactly what you want.
There is a special view for your case. ImageSwitcher is what you need. There is an example from android developers on how to use it. It should be trivial to adapt the example to your needs.