I have a textual WebView that I'm trying to re-size the contents of each time the user puts next text in. The idea is to fit the WebView into the screen without it getting cut off and without showing the scroll bars.
Currently the only reliable method that I have is extending the WebView class and using the base class's protected methods that measure the scrolling bar distances:
public class ExtendedWebView extends WebView {
public ExtendedWebView(Context context) {
super(context);
}
public ExtendedWebView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public int getHorRange() {
return computeHorizontalScrollRange();
}
public int getVerRange() {
return computeVerticalScrollRange();
}
public int getScreenX() {
return computeHorizontalScrollExtent();
}
public int getScreenY() {
return computeVerticalScrollExtent();
}
}
The only limitation I found is if the content is smaller and doesn't get out of bounds, there's no way to know how small it is, as it will return the size of the WebView.
Under another thread, I have the constantly looping method that checks if the WebView is out of bounds, and then re-sizes it. Here's the associated code, simplified:
ExtendedWebView webView; // assume this is initialized
while (true) {
int a, b, c, d;
a = webView.getHorRange();
b = webView.getScreenX();
c = webView.getVerRange();
d = webView.getScreenY();
if (a > b || c > d) {
//shrinking to accommodate text...
int val, x, y;
x = a - b;
y = c - d;
val = x > y ? x : y;
val*=0.1;
val = val == 0 ? 1 : val;
scale-=val;
}
else{
//TODO enlarging webView
}
if (scale <= 50) {
scale = 50;
//prevent from webView being too small
}
if (scale > 200)
scale = 200;
webView.setInitialScale(scale);
}
The shrinking of the WebView works, what I can't figure out is how to enlarge it again once the text has been erased. Whenever I tried simply increasing the scale from the else block, it simply alternates between enlarging and shrinking the text, and the limitation of not knowing the size of the content while it has no scroll bars hinders me from making better solutions.
To clarify, it's not the WebView that I'm trying to re-size, but it's contents.
Related
b4.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (b4.getCompoundDrawables() == null) {
if (turn == 1) {
turn = 2;
b4.setBackgroundResource(R.drawable.iks);
} else if (turn == 2) {
turn = 1;
b4.setBackgroundResource(R.drawable.oks);
}
}
}
});
I have drawable iks (X) and oks (O) (making X-O game), and I wanna use my IF command to check if b4 (button) already have background drawn on it, in order to be able to make only 1 change to button so you can't use button that is already been used again.
You can use getBackground to see whether the background drawable has been set:
if (b4.getBackground() == null)
However, you should really design a "model" for your tic-tac-toe game.
Here's an idea:
Store a 2D int array that can store three possible values: 0, 1 and 2. 0 means nothing in the square. 1 means there is a cross and 2 means there is a nought. You can create constants for these:
public static final int EMPTY = 0;
public static final int CROSS = 1;
public static final int NOUGHT = 2;
Expose a method called updateArray(int x, int y, int value) that updates the value at the specified x and y position.
Each time you call this method, set the drawable of the correct view.
Now to check whether there is nothing in a "square", you can just check the array for EMPTY.
I am trying to make the ImageView missile1 to spawn at a random y-coordinate on the right edge of the screen and then move it left across the screen until it is off the screen, and finally move it back to a random y-coordinate on the right edge of the screen to repeat the process in an infinite loop. The code I have spawns the image at a totally random x and y coordinate location whenever the activity is restarted, but it doesn't move. Can anybody see why it doesn't move and give a solution to my problem? Some example code will be appreciated.
The thread that generates x and y coordinate values:
public class Missiles extends Thread {
private int height, width, currentscore;
private ImageView missile1, missile2, missile3, missile4, missile5;
Handler updatemissile = new Handler();
int min = 100;
RelativeLayout layout;
int setx1, sety1, setx2, sety2, setx3, sety3, setx4, sety4, setx5, sety5;
private Random rand = new Random();
TextView x, y;
Missiles(int screenheight, int screenwidth, ImageView missile01, ImageView missile02, ImageView missile03,
ImageView missile04, ImageView missile05, RelativeLayout rl, TextView xtext, TextView ytext) {
missile1 = missile01;
missile2 = missile02;
missile3 = missile03;
missile4 = missile04;
missile5 = missile05;
height = screenheight;
width = screenwidth;
layout = rl;
x = xtext;
y = ytext;
}
public void updatevalue(int value) {
currentscore = value;
}
public void run() {
SetMissilePosition position = new SetMissilePosition(missile1, layout, x, y);
updatemissile.post(position);
//width is the width of the screen as height is the height of the screen
while (true) {
setx1 = width;
position.updateX(setx1);
int randomNum = rand.nextInt((height - min) + 1) + min;
sety1 = randomNum;
position.updateY(sety1);
while (setx1 > -50) {
setx1 = setx1 - 1;
position.updateX(setx1);
}
setx1 = width;
position.updateX(setx1);
}
}
}
The Runnable Class:
public class SetMissilePosition implements Runnable{
int x1,y1, updateindicator;
ImageView missile1;
RelativeLayout layout;
TextView x,y;
SetMissilePosition(ImageView missile01, RelativeLayout relativeLayout, TextView xtext, TextView ytext) {
missile1 = missile01;
layout = relativeLayout;
x = xtext;
y = ytext;
}
public void updateX( int setx1) {
x1 = setx1;
updateindicator = 1;
}
public void updateY(int sety1) {
y1 = sety1;
updateindicator = 1;
}
public void run() {
missile1.setImageResource(R.drawable.missileanim);
layout.addView(missile1);
if(updateindicator == 1) {
missile1.setX(x1);
missile1.setY(y1);
x.setText(Integer.toString(x1));
y.setText(Integer.toString(y1));
updateindicator = 0;
}
}
}
If I am being unclear in any way or you need more code such as my MainActivity, please ask. Thanks in advance!
It sounds like you're trying to update the UI thread from off of the UI thread.
If that's the route you want to go, I'd encourage you to take a look at the 'run on ui thread' operation. Essentially, when you want to update layout positions, you can just call getContext().runOnUiThread([runnable]) and that will update your position. See here for more information: https://developer.android.com/training/multiple-threads/communicate-ui.html Some things to bear in mind:
Android runs at 60fps, so you need to make sure you're updating every 16ms or so in order to match in sync with your framerate. Otherwise your speed will be off.
Instead of doing that, why not use a Translate animation: https://developer.android.com/reference/android/view/animation/TranslateAnimation.html
You can set starting and ending x and y coordinates, you can even use them as percentages of screenwidth and length, and set the animation to repeat infinitely. If repeating infinitely won't work because you want to pick new X (or Y) values each time, you can hook something up on the end of the animation to pick new values and restart.
I found the answer myself through lots of trial end error. What I did was Created an activity that extended surface view, added a bitmap to it, updated the images coordinates in a thread, and finally passed the coordinates into another thread which updated the screen 30 times a second.
Greetings one and all,
So I am using onDraw in a custom View class to draw shapes on a RelativeLayout + TableLayout, all that works fine.
Inside the MotionEvent's ACTION_MOVE I pass the X & Y to my CustomView and call a method that returns a unique ID value for each set of coordinates / shape respectively.
Due to dragging my finger from one Shape to another: multiple values will be returned to the Console as expected and I would like to store these values in an Arrayfor future use. The issue I am having is that when I try to store the values, it only stores one value in a case where the console shows 4.
Console output looks like:
06-22 07:28:56.173 zar.myapp I/System.out: value: 354
06-22 07:28:56.193 zar.myapp I/System.out: value: 858
06-22 07:28:56.213 zar.myapp I/System.out: value: 989
06-22 07:28:56.213 zar.myapp I/System.out: value: 789
Code:
ArrayList XYcoords = new ArrayList();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
return true;
case MotionEvent.ACTION_MOVE:
///Single value for each finger movement.
DotView endView = getChildForTouch((TableLayout) v, x, y);
XYcoords.add(endView.getId() );
break;
case MotionEvent.ACTION_UP:
///I tried adding them from here as well.
///When I check the size of `XYcoords` it allways return 1
System.out.println("Array Size: " + XYcoords.size());
break;
DotView class: in my MainActivity where I construct my table grid I call: Dotview.setId(i); to assign a unique ID to each cell (verified). (I only shared the relevant codes):
private static class DotView extends View {
private static final int DEFAULT_SIZE = 100;
private Paint mPaint = new Paint();
private Rect mBorderRect = new Rect();
private Paint mCirclePaint = new Paint();
private int mRadius = DEFAULT_SIZE / 4;
int id;
public DotView(Context context) {
super(context);
mPaint.setStrokeWidth(2.0f);
mPaint.setStyle(Style.STROKE);
mPaint.setColor(Color.RED);
mCirclePaint.setColor(Color.CYAN);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.parseColor("#0099cc"));
mBorderRect.left = 0;
mBorderRect.top = 0;
mBorderRect.right = getMeasuredWidth();
mBorderRect.bottom = getMeasuredHeight();
canvas.drawRect(mBorderRect, mPaint);
canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2,
mRadius, mCirclePaint);
}
public void setId(Int id){
this.id = id;
}
public int getId()
{
return this.id;
}
}
getChildForTouch
private DotView getChildForTouch(TableLayout table, float x, float y) {
final int childWidth = ((TableRow) table.getChildAt(0))
.getChildAt(0).getWidth();
final int childHeight = ((TableRow) table.getChildAt(0))
.getChildAt(0).getHeight();
// find out the row of the child
int row = 0;
do {
if (y > childHeight) {
row++;
y -= childHeight;
} else {
break;
}
} while (y > childHeight);
int column = 0;
do {
if (x > childWidth) {
column++;
x -= childWidth;
} else {
break;
}
} while (x > childWidth);
return (DotView) ((TableRow) table.getChildAt(row))
.getChildAt(column);
}
More code can be supplied on request.
Thanks in advance
The issue I am having is that when I try to store the values, it only
stores one value in a case where the console shows 4
I'm assuming you're creating the XYcoords list inside the onTouch() callback, in which case it will have a size of 1 corresponding to the current touch event. As the user moves his finger onTouch() will be called again with another touch event, at this point you'll create a new XYcoords list with one entry and so on...
To solve this, move the XYcoords list initialization outside of the onTouch() callback, like a field in the class. This way you'll not recreate it every time a touch event happens.
As a side note, I'm assuming that with your current code you're trying to store in a list the DotViews the user has touched as he moved his finger. In this case you'll most likely not want to simply do XYcoords.add(endView.getId()) in MotionEvent.ACTION_MOVE as you'll end up with hundreds of duplicate entries(as there will be a lot of touch events that will be triggered inside a single DotView). Instead don't allow duplicates:
if (XYcoords.size() != 0) {
// check to see if we aren't trying to add the same DotView reference
// (meaning we are still inside the same DotView)
int lastId = XYcoords.get(XYcoords.size() - 1);
if (lastId != endView.getId()) {
// not the same DotView so add it
XYcoords.add(endView.getId());
}
} else {
XYcoords.add(endView.getId()); // first event, must add it
}
In the case for MotionEvent.ACTION_UP you need to use(or store somewhere else the info in XYcoords) the data in XYcoords and clear it so next time the user starts touching the DotViews you have a fresh history(no items in XYcoords).
I'm trying to make a flip effect with java swing, but my flip method doesn't look like a real flip.In my method I change the width with the x var and the xspeed, making the image smaller and then check if the x is >= as half of my image. hope somebody could help me improve it thanks in advance.
Animation Class
public class Animation extends JPanel implements MouseListener {
private static final long serialVersionUID = 3264508834913061718L;
public Timer timer;
public int x = 0;
public int xspeed = 2;
public boolean turning = true;
public String pic = "/images/image.jpg";
public URL url = this.getClass().getResource(pic);
public ImageIcon im = new ImageIcon(url);
public String rev = "/images/image2.jpg";
public URL url2 = this.getClass().getResource(rev);
public ImageIcon reverse = new ImageIcon(url2);
public Animation(){
this.setPreferredSize(new Dimension(128,128));
this.setBackground(Color.BLACK);
this.setFocusable(true);
this.addMouseListener(this);
}
public void paintComponent(Graphics g){
super.paintComponent(g);
g.drawImage(im.getImage(), 0 , 0, im.getIconWidth() - x, im.getIconHeight(), null);
}
public void flipAnimation(){
ActionListener actionListener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
//
if (turning) {
x = x + xspeed;
}
repaint();
// x in the middle paint new image & set turning to false to stop animation
if(x >= im.getIconWidth()/2){
turning = false;
x = 0;
im = reverse;
}
}
};
if (turning) {
if (timer != null)timer.stop();
timer = new Timer(30, actionListener);
timer.start();
}
}
#Override
public void mouseClicked(MouseEvent e) {
// TODO Auto-generated method stub
e.getSource();
flipAnimation();
}
First, I’m guessing you want your image to “fold in” from both sides, so you’ll want to increase the image’s left edge along with its right edge:
g.drawImage(im.getImage(), x / 2 , 0, im.getIconWidth() - x, im.getIconHeight(), this);
Notice the first int argument has been changed from 0 to x / 2. Also, it is good practice to pass this as the ImageObserver argument when drawing images in a paintComponent method, since the component itself is the object which is interested in repainting itself when the image has finished loading in the background.
Second, change this:
if (turning) {
x = x + xspeed;
}
to this:
x = x + xspeed;
You don’t need a flag to control the animation. (The correct way to stop the animation is to call timer.stop(), which I’ll get to in a moment.)
Third, change this:
if(x >= im.getIconWidth()/2){
turning = false;
x = 0;
to this:
if(x >= im.getIconWidth()){
xspeed = -xspeed;
As I said, the turning flag is not needed.
The reason for comparing x to the image’s width, instead of half of the width, is that we want to change the image only when the first image has completely “folded in.”
The negation of xspeed reverses the animation.
Finally, at the end of your actionPerformed method, you’ll want to add this:
if (x <= 0) {
timer.stop();
}
This halts the animation when the reverse image reaches its full size, which is why the turning flag is not needed.
I am using code similar to Java - get pixel array from image to get low-level access to pixel data of a BMP image, along the lines of:
BufferedImage image = ImageIO.read(is);
DataBuffer buffer = image.getRaster().getDataBuffer();
byte[] rawPixels = ((DataBufferByte) buffer).getData();
The resulting array is laid bottom to top (ie. its first bytes are the beginning of the last image line), which makes sense considering that BMP files usually have the same layout.
I would like to hide this low-level detail from callers by flipping the lines in this situation. Is there a way I can query the pixels orientation/layout of the loaded BufferedImage?
I have checked the source code of the Java 7 BMPImageReader, and it does translate from bottom-up to top-down order while reading, as I expected it to do. The DataBuffers backing array will thus be in the normal top-down order. I cannot reproduce this behavior using Oracle Java 7 JRE on Windows.
The OP has verified that the problem was indeed in another part of the code, not posted as part of the question.
I think what is described just might be possible, using a special subclass of SampleModel that translates all incoming y-coordinates, but there's no standard method to query for orientation (all Rasters are assumed to be top-down).
Anyway, just for fun, I created some code, to test if it is at all possible. Below is a fully runnable example.
public class SampleModelOrientationTest {
public static void main(String[] args) {
BufferedImage image = new BufferedImage(16, 9, BufferedImage.TYPE_3BYTE_BGR);
WritableRaster raster = image.getRaster();
DataBuffer dataBuffer = raster.getDataBuffer();
SampleModel sampleModel = image.getSampleModel();
QueryingDataBuffer queryBuffer = new QueryingDataBuffer(dataBuffer, sampleModel.getWidth(), sampleModel.getNumDataElements());
sampleModel.getDataElements(0, 0, null, queryBuffer);
System.out.println(queryBuffer.getOrientation());
queryBuffer.resetOrientation();
SampleModel bottomUpSampleModel = new BottomUpSampleModel(sampleModel);
bottomUpSampleModel.getDataElements(0, 0, null, queryBuffer);
System.out.println(queryBuffer.getOrientation());
}
private static class QueryingDataBuffer extends DataBuffer {
enum Orientation {
Undefined,
TopDown,
BottomUp,
Unsupported
}
private final int width;
private final int numDataElements;
private Orientation orientation = Orientation.Undefined;
public QueryingDataBuffer(final DataBuffer dataBuffer, final int width, final int numDataElements) {
super(dataBuffer.getDataType(), dataBuffer.getSize());
this.width = width;
this.numDataElements = numDataElements;
}
#Override public int getElem(final int bank, final int i) {
if (bank == 0 && i < numDataElements && isOrientationUndefinedOrEqualTo(Orientation.TopDown)) {
orientation = Orientation.TopDown;
}
else if (bank == 0 && i >= (size - (width * numDataElements) - numDataElements) && isOrientationUndefinedOrEqualTo(Orientation.BottomUp)) {
orientation = Orientation.BottomUp;
}
else {
// TODO: Expand with more options as apropriate
orientation = Orientation.Unsupported;
}
return 0;
}
private boolean isOrientationUndefinedOrEqualTo(final Orientation orientation) {
return this.orientation == Orientation.Undefined || this.orientation == orientation;
}
#Override public void setElem(final int bank, final int i, final int val) {
}
public final void resetOrientation() {
orientation = Orientation.Undefined;
}
public final Orientation getOrientation() {
return orientation;
}
}
// TODO: This has to be generalized to be used for any BufferedImage type.
// I justy happen to know that 3BYTE_BGR uses PixelInterleavedSampleModel and has BGR order.
private static class BottomUpSampleModel extends PixelInterleavedSampleModel {
public BottomUpSampleModel(final SampleModel sampleModel) {
super(sampleModel.getDataType(), sampleModel.getWidth(), sampleModel.getHeight(),
sampleModel.getNumDataElements(), sampleModel.getNumDataElements() * sampleModel.getWidth(),
new int[] {2, 1, 0} // B, G, R
);
}
#Override public Object getDataElements(final int x, final int y, final Object obj, final DataBuffer data) {
return super.getDataElements(x, getHeight() - 1 - y, obj, data);
}
#Override public int getSample(final int x, final int y, final int b, final DataBuffer data) {
return super.getSample(x, getHeight() - 1 - y, b, data);
}
}
}