I'm working on a painting application for Android and I'd like to use raw data from the device's touch screen to adjust the user's paint brush as they draw. I've seen other apps for Android (iSteam, for example) where the size of the brush is based on the size of your fingerprint on the screen. As far as painting apps go, that would be a huge feature.
Is there a way to get this data? I've googled for quite a while, but I haven't found any source demonstrating it. I know it's possible, because Dolphin Browser adds multi-touch support to the Hero without any changes beneath the application level. You must be able to get a 2D matrix of raw data or something...
I'd really appreciate any help I can get!
There are some properties in the Motion Event class. You can use the getSize() method to find the size of the object. The Motion Event class also gives access to pressure, coordinates etc...
If you check the APIDemos in the SDK there's a simple paitning app called TouchPaint
package com.example.android.apis.graphics;
It uses the following to draw on the canvas
#Override public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
mCurDown = action == MotionEvent.ACTION_DOWN
|| action == MotionEvent.ACTION_MOVE;
int N = event.getHistorySize();
for (int i=0; i<N; i++) {
//Log.i("TouchPaint", "Intermediate pointer #" + i);
drawPoint(event.getHistoricalX(i), event.getHistoricalY(i),
event.getHistoricalPressure(i),
event.getHistoricalSize(i));
}
drawPoint(event.getX(), event.getY(), event.getPressure(),
event.getSize());
return true;
}
private void drawPoint(float x, float y, float pressure, float size) {
//Log.i("TouchPaint", "Drawing: " + x + "x" + y + " p="
// + pressure + " s=" + size);
mCurX = (int)x;
mCurY = (int)y;
mCurPressure = pressure;
mCurSize = size;
mCurWidth = (int)(mCurSize*(getWidth()/3));
if (mCurWidth < 1) mCurWidth = 1;
if (mCurDown && mBitmap != null) {
int pressureLevel = (int)(mCurPressure*255);
mPaint.setARGB(pressureLevel, 255, 255, 255);
mCanvas.drawCircle(mCurX, mCurY, mCurWidth, mPaint);
mRect.set(mCurX-mCurWidth-2, mCurY-mCurWidth-2,
mCurX+mCurWidth+2, mCurY+mCurWidth+2);
invalidate(mRect);
}
mFadeSteps = 0;
}
Hope that helps :)
I'm working on something similar, and I'd suggest looking at the Canvas and Paint classes as well. Looking at getHistorySize() in Motion Event might also be helpful for figuring out how long a particular stroke has been in play.
Related
What I want to do is to click a space on the screen, get the coordinates and paint the image here. I did this using paintIcon and mouseListener in eclipse but how to do the same thing in android studio? Thanks!
public static Game game = new Game();
public Control(){
this.setLayout(null);
this.setBounds(0, 0, 780, 780);
addMouseListener(this);
}
public void paint(Graphics g){
setBackground(Color.GREEN);
board.paintIcon(this, g, 0, 0);
for(int i = 0; i < 19; i++){
for(int j = 0; j < 19; j++){
if(game.gameBoard.board[i][j] != 'E'){
if(game.gameBoard.board[i][j] == 'B'){
black.paintIcon(this, g, j * 40, i * 40);
}
if(game.gameBoard.board[i][j] == 'W'){
white.paintIcon(this, g, j * 40, i * 40);
}
}
}
}
}
#Override
public void mouseClicked(MouseEvent e) {
// TODO Auto-generated method stub
mouseX = e.getX();
mouseY = e.getY();
int targetX = mouseX / 40;
int targetY = mouseY / 40;
game.move(targetX, targetY);
repaint();
}
In Android you use a different system than in "Java"(you know Android is Java), for instance as is clear from your code you do not have a Mouse but a Touch event.
The first thing you should do is to study this official link that explains the concept of Canvas. Then you will learn how to integrate the "tap" into your app that is instead this official link
This is an excellent post to learn ho to do it.
You begin extending the View and overriding the method onDraw(Canvas canvas) then you need to override a listener that say you when the user clicked the screen onTouchEvent(MotionEvent event) usually there are three motion events you want to cover MotionEvent ACTION_DOWN(you tap and the screen recognizes the X,Y coordinates, that is the one you need),ACTION_MOVE(if you drag your finger maintaining pressure on the screen) and ACTION_UP(when you release your finger)
Just remember you need to call invalidate() if you have an animation or something changes on the screen. Basically is a "forced" call to the "onDraw" method. On the basis of the three links I sent you can cover all thebasis of the 2d graphics in Android that is a bit different by Java because of the different features but also the Android dependency on the specific SDK classes
what I want to do is to click a space on the screen, get the coordinates and paint the image here
Here is an example of how you can obtain that in Android instead of "just" Java, please see the notes comments I did below with the double slashes //
public class YourBoard extends View {//EXTENDS VIEW this is important because you override methods
Drawable editIcon = getResources().getDrawable(R.drawable.icon);
Bitmap mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.background);
float xPos = 0;
float yPos = 0;
public YourBoard (Context context) {//HERE THE CONSTRUCTOR YOU CAN INITIALIZE THINGS HERE
super (context);
}
#Override
protected void onDraw (Canvas canvas) {//This was your paint(Graphics g)
super.onDraw(canvas);
canvas.save();
canvas.drawBitmap(mBitmap, 0, 0, null);
canvas.translate(xPos, yPos);
editIcon.draw(canvas);
canvas.restore();
}
#Override
public boolean onTouchEvent (MotionEvent event) {//THIS WAS YOUR MOUSE LISTENER
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN :
xPos = event.getX();
yPos = event.getY();
invalidate();//NOTICE THE INVALIDATE I MENTIONED
break;
}
return true;
}
}
}
I am using MPAndroidChart for displaying a line chart for my application. What I already have is a double tap listener for the whole chart.
Is there any way to obtain double tap listener control for an individual point in line chart? My best guess is to directly obtain the View object of the point and implement a gesture listener for the same. If that is plausible, how can I achieve that?
If this is not possible using MPAndroidChart, is there any other library that might help me with this?
Your solution would involve creating a class that implements OnChartGestureListener
You can immediately see there is a method:
void onChartDoubleTapped(android.view.MotionEvent me);
You would have to implement this method with the functionality you want. It would probably involve getting the raw pixel touch points from the MotionEvent and converting them to x and y values on the chart. There is instructions on how to do that in this answer and in the code below:
#Override
public void onChartDoubleTapped(MotionEvent me) {
float tappedX = me.getX();
float tappedY = me.getY();
MPPointD point = mChart.getTransformer(YAxis.AxisDependency.LEFT).getValuesByTouchPoint(tappedX, tappedY);
Log.d(TAG, "tapped at: " + point.x + "," + point.y);
}
Further examples of a custom OnChartGestureListener are inside the example project here
David Rawson's answer is just perfect and answers the question leaving no stones unturned. However, for my situation, I found the answer by firegloves more useful. I actually get the Entry object using firegloves' solution to obtain values from. Following is the code for what solved my problem but please do note that this solution is hacky and is not advised until absolutely necessary. If you want a clean solution, David Rawson's answer already covers that.
mChart.setOnChartValueSelectedListener(new OnChartValueSelectedListener() {
#Override
public void onValueSelected(Entry e, Highlight h) {
final long selectionTime = System.currentTimeMillis();
final Highlight copyHighlight = h;
final Entry copyEntry = e;
final boolean[] isTapped = {false};
mChart.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
if (!isTapped[0]) {
final long doubleTapTime = System.currentTimeMillis();
if (doubleTapTime - selectionTime < 300) {
if ((motionEvent.getX() <= copyHighlight.getDrawX() + 50
&& motionEvent.getX() >= copyHighlight.getDrawX() - 50)
&& (motionEvent.getY() <= copyHighlight.getDrawY() + 50
&& motionEvent.getY() >= motionEvent.getY() - 50)) {
if (copyEntry.getX() == 1.0f && copyEntry.getY() == 1.0f) {
Toast.makeText(MainActivity.this, "1,1", Toast.LENGTH_SHORT).show();
} else if (copyEntry.getX() == 2.0f && copyEntry.getY() == 2.0f) {
Toast.makeText(MainActivity.this, "2,2", Toast.LENGTH_SHORT).show();
} else if (copyEntry.getX() == 3.0f && copyEntry.getY() == 3.0f) {
Toast.makeText(MainActivity.this, "3,3", Toast.LENGTH_SHORT).show();
}
}
}
}
isTapped[0] = true;
return false;
}
});
}
#Override
public void onNothingSelected() {
}
});
The code snippet above let me work with the Entry object which was associated with the point that was double tapped. Also, I have set a range of 100 pixels while the point was double tapped for the convenience of fat-finger tapping.
I'd love to know if there is a clean solution for double tapping a value while obtaining its Entry data.
Deeping into LineChartRenderer I think it's not possible because graph rendering is reached drawing directly on Canvas like follows:
canvas.drawPath(mCirclePathBuffer, mRenderPaint);
You could try to print all your graph children to check if I'm wrong, something like:
int size =lineChart.getChildCount();
for (int i=0; i<size; i++) {
View v = lineChart.getChildAt(i);
Log.i("### LINE", "### INSTANCE OF " + v.getClass());
}
If there are children views they must be accessible it this manner.
Otherwise you could try to intercept double tap on your entire LineGraph, then you could try to check if tap position correspond to a mapped position but I think you must do the entire work on your own.
Another options could be to implement OnChartValueSelectedListener and emulate double tap with a timer mechanism.
I don't think there is a library that supports this functionality.
Let me know if you find a solution please
I've generated 3D overlay using jMonkey in my Android app. Everything works fine - my ninja model is walking in loop. Awsome !
Now I want to rotate camera according to direction of the phone. I thought compass is the best way BUT unfortunatly I have a lot of problems. So here I go
I've created method that is invoked in activity
public void rotate(float x, float y, float z) {
Log.d(TAG, "simpleUpdate: Nowa rotacja: " + y);
newX = x;
newY = y;
newZ = z;
newPosition = true;
}
in 'simpleUpdate' method I've managed it this way
if(newPosition && ninja != null) {
Log.d(TAG, "simpleUpdate: rotacja: " + newY);
ninja.rotate((float)Math.toRadians(newX), (float)Math.toRadians(newY), (float)Math.toRadians(newZ));
newPosition = false;
}
in my activity I'm checking if phone moved
if(lastAzimuth != (int)azimuthInDegress) {
lastAzimuth = (int)azimuthInDegress;
I cast to int so the distortion won't be so big problem
if ((com.fixus.towerdefense.model.SuperimposeJME) app != null) {
((com.fixus.towerdefense.model.SuperimposeJME) app).rotate(0f, azimuthInDegress, 0f);
}
At the moment I want to rotate it only in Y axis
Now the main problem is that the rotations is more like jump that rotation. When I move my phone a bit and I have 6 degrees diffrence (i see this in my log) the model is rotated like for 90 degrees and he turns back. This has nothing to do with rotation or change taken from my compas.
Any ideas ?
UPDATE
I think I got it. Method rotate, rotates from current state with value I've set. So it looks more like old Y rotate + new value. So I'm setting the diffrence between current value and old value and it now look almoust fine. Is it the good way ?
I want to ask about some ideas / study materials connected to binarization. I am trying to create system that detects human emotions. I am able to get areas such as brows, eyes, nose, mouth etc. but then comes another stage -> processing...
My images are taken in various places/time of day/weather conditions. It's problematic during binarization, with the same treshold value one images are fully black, other looks well and provide me informations I want.
What I want to ask you about is:
1) If there is known way how to bring all images to the same level of brightness?
2) How to create dependency between treshold value and brightness on image?
What I have tried for now is normalize the image... but there are no effects, maybe I'm doing something wrong. I'm using OpenCV (for android)
Core.normalize(cleanFaceMatGRAY, cleanFaceMatGRAY,0, 255, Core.NORM_MINMAX, CvType.CV_8U);
EDIT:
I tried adaptive treshold, OTSU - they didnt work for me. I have problems with using CLAHE in Android but I managed to implement Niblack algorithm.
Core.normalize(cleanFaceMatGRAY, cleanFaceMatGRAY,0, 255, Core.NORM_MINMAX, CvType.CV_8U);
nibelBlackTresholding(cleanFaceMatGRAY, -0.2);
private void nibelBlackTresholding(Mat image, double parameter) {
Mat meanPowered = image.clone();
Core.multiply(image, image, meanPowered);
Scalar mean = Core.mean(image);
Scalar stdmean = Core.mean(meanPowered);
double tresholdValue = mean.val[0] + parameter * stdmean.val[0];
int totalRows = image.rows();
int totalCols = image.cols();
for (int cols=0; cols < totalCols; cols++) {
for (int rows=0; rows < totalRows; rows++) {
if (image.get(rows, cols)[0] > tresholdValue) {
image.put(rows, cols, 255);
} else {
image.put(rows, cols, 0);
}
}
}
}
The results are really good, but still not enough for some images. I paste links cuz images are big and I don't want to take too much screen:
For example this one is tresholded really fine:
https://dl.dropboxusercontent.com/u/108321090/a1.png
https://dl.dropboxusercontent.com/u/108321090/a.png
But bad light produce shadows sometimes and this gives this effect:
https://dl.dropboxusercontent.com/u/108321090/b1.png
https://dl.dropboxusercontent.com/u/108321090/b.png
Do you have any idea that could help me to improve treshold of those images with high light difference (shadows)?
EDIT2:
I found that my previous Algorithm is implemented in wrong way. Std was calculated in wrong way. In Niblack Thresholding mean is local value not global. I repaired it according to this reference http://arxiv.org/ftp/arxiv/papers/1201/1201.5227.pdf
private void niblackThresholding2(Mat image, double parameter, int window) {
int totalRows = image.rows();
int totalCols = image.cols();
int offset = (window-1)/2;
double tresholdValue = 0;
double localMean = 0;
double meanDeviation = 0;
for (int y=offset+1; y<totalCols-offset; y++) {
for (int x=offset+1; x<totalRows-offset; x++) {
localMean = calculateLocalMean(x, y, image, window);
meanDeviation = image.get(y, x)[0] - localMean;
tresholdValue = localMean*(1 + parameter * ( (meanDeviation/(1 - meanDeviation)) - 1 ));
Log.d("QWERTY","TRESHOLD " +tresholdValue);
if (image.get(y, x)[0] > tresholdValue) {
image.put(y, x, 255);
} else {
image.put(y, x, 0);
}
}
}
}
private double calculateLocalMean(int x, int y, Mat image, int window) {
int offset = (window-1)/2;
Mat tempMat;
Rect tempRect = new Rect();
Point leftTop, bottomRight;
leftTop = new Point(x - (offset + 1), y - (offset + 1));
bottomRight = new Point(x + offset, y + offset);
tempRect = new Rect(leftTop, bottomRight);
tempMat = new Mat(image, tempRect);
return Core.mean(tempMat).val[0];
}
Results for 7x7 window and proposed in reference k parameter = 0.34: I still can't get rid of shadow on faces.
https://dl.dropboxusercontent.com/u/108321090/b2.png
https://dl.dropboxusercontent.com/u/108321090/b1.png
things to look at:
http://docs.opencv.org/java/org/opencv/imgproc/CLAHE.html
http://docs.opencv.org/java/org/opencv/imgproc/Imgproc.html#adaptiveThreshold(org.opencv.core.Mat,%20org.opencv.core.Mat,%20double,%20int,%20int,%20int,%20double)
http://docs.opencv.org/java/org/opencv/imgproc/Imgproc.html#threshold(org.opencv.core.Mat,%20org.opencv.core.Mat,%20double,%20double,%20int) (THRESH_OTSU)
After getting the calculator application to work I decided to try to create pong. There is a box in the center and two paddles on both ends. The phone is horizontal. I have the box bouncing off the walls and the paddle moves with me moving my finger down. My problem is i want to make it two player and i want to have multiple finger input for the game. I want one finger to move paddle 1 and the other to move paddle 2. So far this is my input code
#Override
public boolean onTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_MOVE: {
// Find the index of the active pointer and fetch its position
float p1y = ev.getY();
if(ev.getX()<300)
{
player1y = p1y;
}
if(ev.getX()>300)
{
player2y = p1y;
}
//player1y = p1y;
invalidate();
break;
}
}
return true;
}
it resides in my surfaceview class. How can i modify the input method or completely get rid of it and change it to accomplish my goal? Also sorry about my variables. Eclipse crashes a lot on me and my laptops touch panel tends to move my cursor so shorter variables seemed viable. p1y is the y of the touch. and player1y and player2y is the y positions of the player1 and player2 paddle.
A MotionEvent can hold multiple pointers. Use getPointerCount() to see how many pointers are touching the screen in the current event. There are alternate versions of getX and getY that take an index from 0-getPointerCount() - 1.
In a more complex app you would want to track fingers by pointer ID, but for something this simple where you are using a cutoff point on the screen you could do something like this in your ACTION_MOVE case:
int pointerCount = ev.getPointerCount();
for (int i = 0; i < pointerCount; i++) {
float x = ev.getX(i);
float y = ev.getY(i);
if (x < 300) {
player1y = y;
} else if (x > 300) {
player2y = y;
}
}
This post from the Android Developers Blog might help if you'd like more information: http://android-developers.blogspot.com/2010/06/making-sense-of-multitouch.html