Resize Default Keyboard in Android Programatically - android

Is it possible to change the size of the default keyboard in Android?
I want to reduce the height of default keyboard.
Please let me know whether it is possible or not? or provide me way to get it.

It's possible create a new class that extends the Keyboard class that defines a public getKeyHeight method and overrides the getHeight method.check below code:
public void setKeyHeight(int height) {
super.setKeyHeight(height);
}
#Override
public int getHeight() {
return getKeyHeight() * 3;
}
2) Defined a new method
double height_modifier = 1.5;
int height = 0;
for(Keyboard.Key key : mKeyboard.getKeys()) {
key.height *= height_modifier;
key.y *= height_modifier;
height = key.height;
}
mKeyboard.setKeyHeight(height);

Related

Why won't the child views of my custom view render in Marshmallow?

I'm developing a custom keyboard using Xamarin.
My keyboard view has an overridden OnDraw() for both the view container itself and it's child key views. I'm also using SetWillNotDraw(false) appropriately for each view. It currently works beautifully in 5.0.1 on my Nexus 10 tablet.
In Android 6.0.1, on a Nexus 6 and a Nexus 6P, the keyboard view correctly draws itself (just a background color). The child key views however are never drawn, even if I iterate through the view hierarchy and force an invalidate on each one. This seems to be specific to Marshmallow.
I don't know if there's something new I need to account for in this version of Android or if I'm encountering a bug.
Any help or suggestions are welcome.
Code:
KeyboardView
KeyView
Some extra details to shed light on the original post:
The three major files we use for keyboard rendering are KeyboardView.cs, KeyboardRowView.cs, and KeyView.cs.
KeyboardView (the container for the whole keyboard)
This has no trouble rendering. KeyboardView extends a LinearLayout and its OnDraw method runs, calling a Build() function to create what it needs (just a basic background which will "hold" the individual keys):
protected override void OnDraw(Canvas canvas)
{
Build();
base.OnDraw(canvas);
// background
Paint bg = new Paint(PaintFlags.AntiAlias);
bg.Color = BG; // light blue
canvas.DrawRect(0, 0, MeasuredWidth, Height, bg);
InvalidateKeys();
}
(...and Build() below...)
public void Build()
{
// only build once
if (keyLayout != null)
return;
// clear out children
RemoveAllViews();
// define sizes of stuff
if (isPortrait)
{
keyMargin = (int)(MeasuredWidth * .01f);
}
else
{
keyMargin = (int)(MeasuredHeight * .01f);
}
keyWidth = (MeasuredWidth - (keyMargin * 2)) / keyboard.MaxCols;
keyHeight = (MeasuredHeight - (keyMargin * 2)) / keyboard.Rows.Count;
// set general padding around keyboardview
SetPadding(keyMargin, keyMargin, keyMargin, keyMargin);
// build KeyLayout from the keyboard object
keyLayout = new List<List<KeyView>>();
int idx = 0;
foreach (List<Key> row in keyboard.Rows)
{
keyLayout.Add(new List<KeyView>());
// create and add new KeyboardRowView
KeyboardRowView krv = new KeyboardRowView(Context, this, idx);
AddView(krv);
// figure out if we need a margin offset for this row
int extraMargin = 0;
int numCols = CountRowCols(row);
if (numCols < keyboard.MaxCols)
{
// measure full width of the button container and the total row margin
int rowWidth = (int)(numCols * keyWidth);
int rowMargin = MeasuredWidth - (keyMargin * 2) - rowWidth;
// add the offset
extraMargin = rowMargin / 2;
}
// build keys and add them to keyLayout and KeyboardRowView
int idx2 = 0;
foreach (Key key in row)
{
int leftMargin = idx2 == 0 ? extraMargin : 0;
KeyView kv = new KeyView(Context, this, key, leftMargin);
keyLayout[idx].Add(kv);
krv.AddView(kv);
idx2++;
}
idx++;
}
}
(As a friendly reminder, we're doing this because we need a custom keyboard which can only display certain keys/commands to our users.)
KeyboardRowView (the container for each row of keys)
This also extends a LinearLayout, and also has its OnDraw method called:
protected override void OnDraw(Canvas canvas)
{
base.OnDraw(canvas);
Paint paint = new Paint();
paint.SetARGB(255, 0, 0, 0);
paint.SetStyle(Paint.Style.Stroke);
paint.StrokeWidth = 3;
canvas.DrawRGB(255, 255, 255);
canvas.DrawRect(0, 0, 100, 100, paint);
}
KeyView (the class which loads and renders each individual key)
KeyView extends View and View.IOnTouchListener. KeyView's constructor is called, but its OnDraw method is never called/executed:
// key views are always dynamically created
public KeyView(Context ctx, KeyboardView parent, Key k, int leftMargin)
: base(ctx)
{
// make sure the key will draw
SetWillNotDraw(false);
keyboard = parent;
key = k;
isDown = false;
// check for an overridden span to adjust width, if needed
int span = string.IsNullOrEmpty(key.Span) ? 1 : Convert.ToInt32(key.Span);
int keyWidth = keyboard.keyWidth + ((span - 1) * keyboard.keyWidth);
width = keyWidth;
height = keyboard.keyHeight;
// set margin
var parameters = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WrapContent,
LinearLayout.LayoutParams.MatchParent
);
parameters.LeftMargin = leftMargin;
LayoutParameters = parameters;
// set touch listener
SetOnTouchListener(this);
// enable haptic feedback for button presses
HapticFeedbackEnabled = true;
}
(...and OnDraw)
protected override void OnDraw(Canvas canvas)
{
base.OnDraw(canvas);
KeyState primary = key.Primary;
KeyState secondary = key.Secondary;
if (keyboard.swapped)
{
primary = key.Secondary != null ? key.Secondary : key.Primary;
secondary = key.Secondary != null ? key.Primary : null;
}
if (keyboard.shifted)
{
if (primary.Shift != null)
primary = primary.Shift;
if (secondary != null && secondary.Shift != null)
secondary = secondary.Shift;
}
// figure out what color the key is supposed to be
Paint bg = new Paint(PaintFlags.AntiAlias);
bg.Color = GetKeyBgColor(key.Style);
if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.Lollipop)
canvas.DrawRoundRect(keyboard.keyMargin, keyboard.keyMargin, width - keyboard.keyMargin, height - keyboard.keyMargin, keyboard.keyMargin, keyboard.keyMargin, bg);
else
canvas.DrawRoundRect(new RectF(keyboard.keyMargin, keyboard.keyMargin, width - keyboard.keyMargin, height - keyboard.keyMargin), keyboard.keyMargin, keyboard.keyMargin, bg);
// draw primary key state
Paint fg = new Paint(PaintFlags.AntiAlias);
fg.TextSize = height * .5f;
fg.Color = GetKeyFgColor(key.Style);
string character = string.IsNullOrEmpty(primary.Character) ? "#" : primary.Character;
int charWidth = Convert.ToInt32(fg.MeasureText(character));
int charX = (width - charWidth) / 2;
canvas.DrawText(character, charX, (height * .7f), fg);
// draw secondary key state
if (secondary != null)
{
fg.TextSize = height * .25f;
fg.Color = GetKeyFgColor(key.Style, true);
character = string.IsNullOrEmpty(secondary.Character) ? "#" : secondary.Character;
charWidth = Convert.ToInt32(fg.MeasureText(character));
charX = width - charWidth - (keyboard.keyMargin * 2);
canvas.DrawText(character, charX, (height * .35f), fg);
}
}
I am confused. Both KeyboardView and KeyboardRowView have a SetWillNotDraw(false); function call in their constructor/initialization methods. KeyView also has the same function call, and successfully receives each key value that needs to be rendered. What I don't get is why it just...won't...draw...the...keyboard. (Argh.) When I spoke with the original poster about this, he told me that all the conditions have been met in order for the keyboard keys to be rendered. I tried attaching breakpoints to see what was preventing KeyView's OnDraw from being called, but got caught up in repeated OnMeasure function calls (and there are a lot of keys that get rendered, so that got old quick).
It's worth mentioning that we've tested it on the latest Nexus 6P smartphone (running stock Android 6.0 Marshmallow) and an old Motorola Droid 4 (with Marshmallow installed via CyanogenMod 13). When we tried it using a Xamarin Android Player emulator (running Marshmallow), it actually worked... my guess is that the emulator might be rendering the keyboard with no problem because the actual phones themselves are either
(a) restricting access somehow
(b) potentially holding on to old code and we just haven't completely removed their old .apks
(c) some other issue I haven't thought of
Thank you for your time. If anyone can think of a possible direction to go in, it would be appreciated!
Fixed by properly implementing OnMeasure and OnLayout for each custom view through the top-down approach suggested by Android documentation.

Is There any way to Predict x and y location from one screen size to another?

i am developing an android app, about joining dots and making picture.
so far i can't find a way to extract exact x and y coordinates of black dots . so I've hard coded x and y locations to draw point on exact black dot of image. It worked wonderful on 1152*720 size ,but problem occurs when i tested it on 480*600 size , dots misplaced from there exact location , now
My Question is if I've written something like :
x = 100 ,
y = 200
(on screen 1152*720)
what will be x and y values in different screen sizes like 480*600 and how to calculate it ? i know it is silly question but i am new to this stuff.
Answering your question as you have asked it...
int oldScreenX // The current x coord
int newScreenX // The new x coord
...
float oldScreenSizeX = 1152.0f;
float newScreenSizeX = 600.0f;
newScreenX = (int)(oldScreenX / oldScreenSizeX) * newScreenSizeX; // Remember to cast the result back to an int
Do the same for y.
Additional:
Perhaps you should reconsider your approach.
The real problem is how do you put the dot in the same location on the Image, if the Image is being drawn at a different size. So forget about measuring the screen size. Measure the Image size instead.
For example, if you are showing your image in an ImageView, you could write a general scaling method like the following:
public int scaleCoordinate(int unscaledImageSize, int scaledImageSize, int unscaledCoordinate) {
scaledCoordinate = (int)(unscaledCoordinate / unscaledImageSize) * scaledImageSize; // Remember to cast the result back to an int
return scaledCoordinate;
}
Then you can use it in your code, something like:
ImageView imageView = (ImageView)findViewById(R.id.my_imageview);
Drawable drawable = image.getDrawable();
// Get the original size of the bitmap
int unscaledSizeX = drawable.getIntrinsicWidth();
// Get the current size it is being drawn at on the screen
int scaledSizeX = imageView.getWidth();
int scaledCoordinateX = scaleCoordinate(unscaledSizeX, scaledSizeX, unscaledCoordinateX);
NOTE:
The ImageView needs to be measured and laid out by the system before calling the above code. If you call it too early imageView.getWidth() will return 0.
It would be best to call the above code once the ImageView is actually displayed on the screen (from your onResume() or later).
I was done this, while studying java may be it will be helpful for you.
import java.applet.Applet;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
public class DrawLineOnGivenLocations extends Applet implements MouseListener, MouseMotionListener{
int x1 = 0, y1 = 0, x2 = 0, y2 = 0;
public void init() {
addMouseListener(this);
addMouseMotionListener(this);
}
public void mouseMoved(MouseEvent me) {
// show status
showStatus("Moving mouse at " + me.getX() + ", " + me.getY());
}
public void mouseClicked(MouseEvent me) {
// save coordinates
x1 = me.getX();
y1 = me.getY();
x2 = me.getX();
y2 = me.getY();
repaint();
}
public void paint(Graphics g){
g.drawLine(x1,y1 ,x2, y2);
}
public void mouseEntered(MouseEvent arg0) {}
public void mouseDragged(MouseEvent arg0) {}
public void mouseExited(MouseEvent arg0) {}
public void mousePressed(MouseEvent arg0) {
//repaint();
}
public void mouseReleased(MouseEvent arg0) {}
}

how to change a soft keyboard's height at run-time?

I'm designing a soft keyboard and I want to change its height at run-time as the user choose between landscape and portrait mode. I know how to change key's height in xml, but I need to do it dynamically.
The only thing that came to my mind was to subclass from Keyboard and override its setKeysHeight (int height), but it seems useless as the whole keyboard stopped responding to my clicks and the height (though different from previously) didn't care about 'height' in the aforementioned function.
Any idea/workaround?
Original solution posted at https://stackoverflow.com/a/9695482/1241783 but it doesn't come with explanation so here I extend it a bit.
1) Create a new class that extends the Keyboard class that overrides the getHeight() method.
#Override
public int getHeight() {
return getKeyHeight() * 3;
}
Note: the number 3 here is your total number of rows, if your keyboard has 5 rows, put 5.
If your keyboard row height is different for each row, here you need to calculate yourself and return the total height (unit is in pixels, took me a while to figure out that it is not dp so need to convert dp to pixel for all calculations) for example:
#Override
public int getHeight() {
return row1Height + row2Height + row3Height + row4Height + row5Height;
}
2) Create a new public function in the same class.
public void changeKeyHeight(double height_modifier)
{
int height = 0;
for(Keyboard.Key key : getKeys()) {
key.height *= height_modifier;
key.y *= height_modifier;
height = key.height;
}
setKeyHeight(height);
getNearestKeys(0, 0); //somehow adding this fixed a weird bug where bottom row keys could not be pressed if keyboard height is too tall.. from the Keyboard source code seems like calling this will recalculate some values used in keypress detection calculation
}
If you're not using height_modifier but set to specific height instead, you'll need to calculate key.y position yourself.
If your keyboard row height is different for each row, you may need to check the keys, determine the row it belongs and set the height to correct value if not the keys will overlap each other. Also store the row heights in private variable to be used in getHeight() above. PS: On certain configuration I couldn't press the bottom row keys after changing keyboard height, and I found that calling getNearestKeys() fixes that though I'm not exactly sure why.
Note: key.y is y position of the key, coordinate 0 starts from the top of the keyboard, and goes down as the value increases. e.g. Coordinate 100 points to 100 pixel from the top of the keyboard :)
3) Last step is to call changeKeyHeight in your main class that extends InputMethodService. Do it inside (override it) onStartInputView() as this is where the keyboard should be redrawn after you change the height (via preference or something).
If you're looking at the Android soft keyboard sample project, it will be like this:
#Override public void onStartInputView(EditorInfo attribute, boolean restarting) {
super.onStartInputView(attribute, restarting);
// Change the key height here dynamically after getting your value from shared preference or something
mCurKeyboard.changeKeyHeight(1.5);
// Apply the selected keyboard to the input view.
mInputView.setKeyboard(mCurKeyboard);
mInputView.closing();
final InputMethodSubtype subtype = mInputMethodManager.getCurrentInputMethodSubtype();
mInputView.setSubtypeOnSpaceKey(subtype);
}
Cheers!
Extra: If you need a dp to pixel converter, here's the code:
private int convertDpToPx(int dp)
{
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());
}
I use the following code with Android 6:
private void adjustKeyboardKeyHeight (MyKeyboard keyboard, int newKeyHeight) {
int oldKeyHeight = keyboard.getKeyHeight();
int verticalGap = keyboard.getVerticalGap();
int rows = 0;
for (Keyboard.Key key : keyboard.getKeys()) {
key.height = newKeyHeight;
int row = (key.y + verticalGap) / (oldKeyHeight + verticalGap);
key.y = row * newKeyHeight + (row - 1) * verticalGap;
rows = Math.max(rows, row + 1);
}
keyboard.setHeight(rows * newKeyHeight + (rows - 1) * verticalGap);
}
private static class MyKeyboard extends Keyboard {
private int height;
MyKeyboard (Context context, int xmlLayoutResId) {
super(context, xmlLayoutResId);
height = super.getHeight();
}
#Override public int getKeyHeight() {
return super.getKeyHeight();
}
#Override public int getVerticalGap() {
return super.getVerticalGap();
}
public void setHeight (int newHeight) {
height = newHeight;
}
#Override public int getHeight() {
return height;
}
}
Bruce's solution does not work anymore. Bottom keys stop responding after resize. Look at Keyboard.java:
public int[] getNearestKeys(int x, int y) {
if (mGridNeighbors == null) computeNearestNeighbors();
So getNearestKeys does not trigger any computations, computeNearestNeighbors() is not called more than once (and it's private, so you can't call it directly).
Instead of changing size in existing object, I modified Keyboard.Key class:
public class FTKey extends Keyboard.Key {
FTKey(Resources res, Keyboard.Row parent, int x, int y, XmlResourceParser parser) {
super(res, parent, x, y, parser);
this.height = (int) (height * FTPref.yScale);
this.y = this.height * FTPref.yScale; // This works only if all rows of the same height
}
In onStartInput() I re-create all keyboard objects anew every time:
FTPref.yScale = 0.7f;
mEnglishKeyboard = new FTKeyboard(this ...
mRussianKeyboard = new FTKeyboard(this ...
Overriding getHeight() to calculate total keyboard height is still required.
It will be tricky to have keys of different height, because you'll have to calculate not only total height, but y position of every button and it depends on the height of previous rows.

Gridview in Android, with dynamic width & height of blocks

I want to create a Gridview just like a GooglePlay. The only difference in our case is the size of width & height should be the based on the popularity.
For example, If I have a array list of categories along with number of popularity. The order of category is fixed, so it will appear on the grid in same order, the only thing is going to change its width and height.
So, If my grid has 2 columns, then blocks should be adjusted automatically.
I have seen the demo of stagerred view but unable to replicate as per my requirement. So If any one has any suggestion will be appreciated.
Thanks
If you want to adjust the width of your block automatically you have to get Display Size.
This code Works for me.
public static void calculateSize(Activity ac) {
DisplayMetrics dMetrics = new DisplayMetrics();
ac.getWindowManager().getDefaultDisplay().getMetrics(dMetrics);
float density = dMetrics.density;
int w = Math.round(dMetrics.widthPixels / density);
int h = Math.round(dMetrics.heightPixels / density);
YourBlock.WIDTH = Math.round((w - (YourBlock.PADDING * (YourBlock.COLUMN + 1))) / YourBlock.COLUMN);
YourBlock.HEIGHT = Math.round(YourBlock.WIDTH * YourBlock.RATIO);
}
In this case:
public static final int YouBlock.COLUMN = 2;
public static float YourBlock.RATIO = 2.0f/3;
public static int YouBlock.PADDING = 4;
Example:
Each item will automacticaly adjusts width with each device (each size)

how to show string depending upon width of the layout

I have created two layout vertically of equal width. And I have string data to be displayed on text view dynamically. When string is greater than the width of the layout then string is wrapped to the width of the layout and for remaining string I want to create a new TV dynamically. This process ends till remaining string finishes. For next string same process continues. When the process reaches bottom of linearlayout1, remaining string should starts from linearlayout2. And the process continues till it reaches bottom of linearlayout2.
I tried like this
private void nextlinechar(int numChars,String devstr) {
nextchar=devstr;
Log.d("char 1",""+nextchar);
TextView sub=new TextView(getApplicationContext());
sub.setLines(1);
sub.setTextColor(Color.BLACK);
sub.setTextSize(textsize);
sub.setText(nextchar);
nextchar=devstr.substring(nextcharstart);
String textToBeSplit = nextchar; // Text you want to split between TextViews
String data=TextMeasure(nextchar,sub);
float myTextSize=sub.getTextSize();
float textView2Width=400;
// String next=TextMeasure(nextchar,sub);
Paint paint = new Paint();
paint.setTextSize(myTextSize); // Your text size
numChars1= paint.breakText(textToBeSplit, true,textView2Width, null);
nextchar1=nextchar.substring(numChars1);
// Log.d("char",""+i+" "+nextchar.length());
main.addView(sub);
nextlinechar(numChars1,nextchar);
}
Illustration
Possible Solution 1
What you need is a FlowLayout, found here. Basically the text needs to wrap around, instead of overflow to the right.
Possible Solution 2
Try to use a webview instead, and populate the text in 2 webviews. That will be faster with lesser code and not as buggy.
I used FlowLayout only when I needed to click on each word separately. Basically a test for grammar where people select the Parts Of Speech of the sentence. For that I needed listener on each word.
Reffer This may be this help you:
package com.example.demo;
import android.content.Context;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.widget.TextView;
public class FontFitTextView extends TextView {
public FontFitTextView(Context context) {
super(context);
initialise();
}
public FontFitTextView(Context context, AttributeSet attrs) {
super(context, attrs);
initialise();
}
private void initialise() {
mTestPaint = new Paint();
mTestPaint.set(this.getPaint());
// max size defaults to the initially specified text size unless it is
// too small
}
/*
* Re size the font so the specified text fits in the text box assuming the
* text box is the specified width.
*/
private void refitText(String text, int textWidth) {
if (textWidth <= 0)
return;
int targetWidth = textWidth - this.getPaddingLeft()
- this.getPaddingRight();
float hi = 100;
float lo = 2;
final float threshold = 0.5f; // How close we have to be
mTestPaint.set(this.getPaint());
while ((hi - lo) > threshold) {
float size = (hi + lo) / 2;
mTestPaint.setTextSize(size);
if (mTestPaint.measureText(text) >= targetWidth)
hi = size; // too big
else
lo = size; // too small
}
// Use lo so that we undershoot rather than overshoot
this.setTextSize(TypedValue.COMPLEX_UNIT_PX, lo);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
int height = getMeasuredHeight();
refitText(this.getText().toString(), parentWidth);
this.setMeasuredDimension(parentWidth, height);
}
#Override
protected void onTextChanged(final CharSequence text, final int start,
final int before, final int after) {
refitText(text.toString(), this.getWidth());
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
if (w != oldw) {
refitText(this.getText().toString(), w);
}
}
// Attributes
private Paint mTestPaint;
}
The following is an idea I came up with (of course I first tried to search for a built-in method of TextViwe which does what you need, but couldn't find any):
Determine how many lines should be displayed in each of the layouts (left and right) depending on the screen size (this.getResources().getDefaultDisplay() and then search for the right method), the desired text size and the spaces between the layouts and between the text and the top/bottom edges. Don't forget you may need to convert pixels to dips or vice versa, as most Android size measure methods return size in pixels.
Set the max number of lines to each of the TVs (tv.setLines()).
Set the entire text to the left TV.
Now the first half of the text will be displayed in the left TV. The other part of the text will be hidden because you've reached the max num of lines in the TV. So, use getText() to receive the text currently displayed in the TV (hopefully, this will return only the first half of the text.. haven't tried that out yet, sorry. If not, maybe there's some getDisplayedText() method or something). Now you can use simple Java methods to retrieve only the remining part of the text from the original text (a substring) and set it to the second TV.
Hope that helps.

Categories

Resources