Предупреждение: Это решение имеет как минимум 5 лет (я все еще использую его), поэтому, безусловно, устарело и, возможно, есть новые технологии, чтобы лучше решить эту проблему.В любом случае, я опубликую решение здесь, чтобы помочь вам.Кроме того, это решение немного обременительно, и вы, возможно, могли бы изменить или улучшить его, не стесняйтесь делать это.
Резюме
Это решение основано на 2 изображениях, одно из которых будет видно(который вы хотите показать пользователю), а другой будет невидимым.
Невидимое изображение должно иметь такой же размер в пикселях, что и целевое изображение (в вашем случае изображение тела), оно будет прозрачным (или черным фоном) и будет иметь разные зоны, которые вы хотите щелкнуть, заполненными различнымиcolors.
Итак, когда вы щелкаете по целевому изображению, проверяются координаты его матрицы, а затем, начиная с этих координат, цвет нажатого пикселя получается из второго изображения.
Поскольку вы знаете, какой цвет соответствует какой части тела (поскольку ранее вы настроили карту с помощью color-> part_of_body), получив один цвет, вы сможете точно узнать, какая часть тела была нажата.
Решение
У вас есть целевое изображение:
Затем вам нужно создать второе изображение следующим образом:
Обратите внимание, что теперь у вас есть каждая часть тела, помеченная отдельным цветом.Обратите внимание, что цвета заметно отличаются.Вот почему, если вы используете похожие цвета, могут быть конфликты в то время, чтобы получить часть тела, потому что цвета могут быть перепутаны.
После этого вам нужно экспортировать изображение цветов с прозрачным фоном, и вы должны получитьследующее (обратите внимание, что фон прозрачный, но фон StackOverflow белый):
Вы будете работать с первым и третьим изображениями,второй - только промежуточный шаг.
Прежде всего, вам нужно настроить в коде цвет вашей карты-> part_of_body:
public HashMap<Integer, String> bodyParts;
bodyParts.put(parseColor("#ff0000"), "part_1");
bodyParts.put(parseColor("#00ff00"), "part_2");
bodyParts.put(parseColor("#0000ff"), "part_3");
bodyParts.put(parseColor("#ffff00"), "part_4");
... // Finish here with all your parts and colors
Затем я сделал пользовательский ImageView
для более удобной обработки изображений:
ZoneTapImageView.java
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import java.util.HashMap;
import uk.co.senab.photoview.PhotoViewAttacher;
public class ZoneTapImageView extends FrameLayout implements PhotoViewAttacher.OnPhotoTapListener {
private static final String LOG_TAG = "ZoneMapTouch";
private static final int DEFAULT_TOLERANCE = 25;
private ImageView imageView;
private ImageView imageViewAreas;
@SuppressLint("UseSparseArrays")
private HashMap<Integer, String> areasMap = new HashMap<>();
private Context context;
private OnAreaObtainedListener areaObtainedListener;
private PhotoViewAttacher imageViewAttacher;
private PhotoViewAttacher imageViewAreasAttacher;
public ZoneTapImageView(Context context) {
super(context);
init(context);
}
public ZoneTapImageView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public ZoneTapImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
private void init(Context context) {
int padding = getResources().getDimensionPixelSize(R.dimen.groups_padding_mini);
this.context = context;
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
this.setLayoutParams(params);
params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
params.gravity = Gravity.CENTER;
imageView = new ImageView(getContext());
imageView.setPadding(padding, padding, padding, padding);
imageView.setLayoutParams(params);
imageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
imageView.setAdjustViewBounds(true);
imageViewAreas = new ImageView(getContext());
imageViewAreas.setPadding(padding, padding, padding, padding);
imageViewAreas.setLayoutParams(params);
imageViewAreas.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
imageViewAreas.setAdjustViewBounds(true);
imageViewAreas.setVisibility(View.INVISIBLE);
this.addView(imageViewAreas);
this.addView(imageView);
}
public void setImageResources(int resIdImage, int resIdImageAreas) {
setImageResource(resIdImage);
setImageResourceAreas(resIdImageAreas);
}
public void setImageResource(final int resIdImage) {
imageView.post(new Runnable() {
@Override
public void run() {
// Here I use a Image Cache, but it's not necessary
ImageMemoryCache.loadBitmap(context, resIdImage, imageView, imageView.getWidth(), imageView.getHeight(), new ImageMemoryCache.OnImageLoadedListener() {
@Override
public void onImageLoaded() {
if (imageViewAttacher == null) {
imageViewAttacher = new PhotoViewAttacher(imageView);
imageViewAttacher.setZoomable(true);
imageViewAttacher.setOnPhotoTapListener(ZoneTapImageView.this);
}
}
});
}
});
}
public void setImageResourceAreas(final int resIdImageAreas) {
imageViewAreas.post(new Runnable() {
@Override
public void run() {
// Here I use a Image Cache, but it's not necessary
ImageMemoryCache.loadBitmap(context, resIdImageAreas, imageViewAreas, imageViewAreas.getWidth(), imageViewAreas.getHeight(), new ImageMemoryCache.OnImageLoadedListener() {
@Override
public void onImageLoaded() {
if (imageViewAreasAttacher == null) {
imageViewAreasAttacher = new PhotoViewAttacher(imageViewAreas);
imageViewAreasAttacher.setZoomable(false);
}
}
});
}
});
}
public void setZoomOut() {
if (imageViewAttacher != null)
imageViewAttacher.setScale(1, true);
}
public void setOnAreaObtainedListener(OnAreaObtainedListener areaObtainedListener) {
this.areaObtainedListener = areaObtainedListener;
}
@Override
public void onPhotoTap(View view, float x, float y) {
if (imageViewAreasAttacher == null) return;
final RectF displayRect = imageViewAreasAttacher.getDisplayRect();
float xAbsolute = x * displayRect.width() + displayRect.left;
float yAbsolute = y * displayRect.height() + displayRect.top;
Log.d("MapTouch", "X: " + xAbsolute + " Y: " + yAbsolute);
getAreaFromCoordinatesAsync((int) xAbsolute, (int) yAbsolute, areaObtainedListener);
}
public void setAreasMap(HashMap<Integer, String> areasMap) {
this.areasMap = areasMap;
}
public void getAreaFromCoordinatesAsync(final int x, final int y, final OnAreaObtainedListener onAreaObtainedListener) {
new Thread(new Runnable() {
@Override
public void run() {
String area = getAreaFromCoordinates(x, y);
if (onAreaObtainedListener != null) {
onAreaObtainedListener.OnArea(area);
}
}
}).start();
}
public String getAreaFromCoordinates(int x, int y) {
int touchColor = getTapColor(x, y);
Log.d(LOG_TAG, "Color (" + x + ", " + y + "): " + touchColor);
if (touchColor == Integer.MIN_VALUE) return null;
return getAreaFromColor(touchColor);
}
public String getAreaFromColor(int color) {
for (Integer colorKey : areasMap.keySet()) {
if (matchColor(colorKey, color, DEFAULT_TOLERANCE)) {
return areasMap.get(colorKey);
}
}
return null;
}
private boolean matchColor(int color1, int color2, int tolerance) {
if (Math.abs(Color.red(color1) - Color.red(color2)) > tolerance)
return false;
if (Math.abs(Color.green(color1) - Color.green(color2)) > tolerance)
return false;
if (Math.abs(Color.blue(color1) - Color.blue(color2)) > tolerance)
return false;
return true;
}
private int getTapColor(int x, int y) {
try {
// Convert coordinates for scaled bitmap
float[] eventXY = new float[]{x, y};
Matrix invertMatrix = new Matrix();
imageViewAreas.getImageMatrix().invert(invertMatrix);
invertMatrix.mapPoints(eventXY);
x = (int) eventXY[0];
y = (int) eventXY[1];
// Get bitmap
Drawable imgDrawable = imageViewAreas.getDrawable();
Bitmap bitmap = ((BitmapDrawable) imgDrawable).getBitmap();
// Get color
return bitmap.getPixel(x, y);
} catch (Exception e) {
return Integer.MIN_VALUE;
}
}
}
Вам понадобится следующая зависимость, чтобы она заработала:
compile 'com.github.chrisbanes.photoview:library:1.2.2'
Чтобы создать экземпляр предыдущего класса, вы можете сделать это следующим образом:
imageView = new ZoneTapImageView(getActivity());
imageView.setImageResources(R.drawable.body_image, R.drawable.invisielb_areas_image);
imageView.setAreasMap(bodyParts);
imageView.setOnAreaObtainedListener(new OnAreaObtainedListener() {
@Override
public void OnArea(final String area) {
Log.d("MapTouch", "Area: " + area);
// Area here is such "part_1" or "part_2"
if (area != null) {
// Handle your bodyPart click
}
}
});
И это все.Надеюсь, я не пропустил ни одного кода и надеюсь, что это поможет вам.