Android: 在屏幕上定位单词。 Google ML Kit 边界框有点偏离
Android: locating words on the screen. Google ML Kit bounding boxes are off a bit
我试图在 phone 屏幕上找到特定的词,然后在它们周围显示一个边界框(如果存在)。我遵循以下步骤:
- 捕获 整个 屏幕内容(使用 MediaProjection API)。
- 将此屏幕截图传递给 Google ML Kit
中的 TextRecognizer
对象
- 检查检测到的单词,如果匹配则使用 ML Kit 返回的
Rect
在屏幕上绘制。
它几乎 有效,这是在记事本应用程序上发现并突出显示单词 hello
的屏幕截图:
如您所见,半透明的黄色盒装不完全 hello
s。
这里是相关的代码示例。将屏幕截图位图传递给 ML Kit:
InputImage image = InputImage.fromBitmap(screenshotBitmap, 0);
//I checked: image, screen, and overlay view dimensions are exactly the same.
TextRecognizer recognizer = TextRecognition.getClient();
recognizer.process(image)
.addOnSuccessListener(this::processText);
processText
获取识别词的方法:
for (Text.Element element : getElements()) {
String elementText = element.getText();
Rect bounds = element.getBoundingBox(); //Getting the bounding box
if (elementText.equalsIgnoreCase("hello")) { //hello is hardcoded for now
addHighlightCard(bounds.left, bounds.top, bounds.width(), bounds.height());
}
}
最后是 addHighlightCard
,它创建并定位您在屏幕截图上看到的视图。它使用带有 RelativeLayout
的全屏覆盖,因为这种布局允许我指定子视图的确切位置和宽度。
public void addHighlightCard(int x, int y, int width, int height) {
View highlightCard = inflater.inflate(R.layout.highlight_card, overlayRoot, false);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(width, height);
params.leftMargin = x;
params.topMargin = y;
highlightCard.setLayoutParams(params);
overlayRoot.addView(highlightCard, params);
}
如您所见,我没有进行任何缩放,我捕获了整个屏幕,并且我使用了填满整个屏幕(甚至是工具栏)的布局。然后,我认为 ML Kit 返回的坐标应该可以直接用于绘制到屏幕上。但显然我错了,图像似乎在某处缩小了,但我不知道在哪里。
解决方案:事实证明,媒体投影 API 虚拟显示的大小不正确导致边界框未对齐。我不会让这个问题变得更长,而是 post a link here to a GitHub repository,在那里你可以找到一个示例应用程序,它显示使用媒体投影 API 并在屏幕截图上执行文本识别的工作方式。
示例应用程序:test-text-recognition
输入 MLKit 并显示在预览中的图像可能具有不同的尺寸。请参阅 mlkit example 了解如何缩放和映射它们。
它更像是下一个调试步骤而不是答案。:
所以你可以看到每个标记之间的边距随着每次高亮调用而增加。
看来您没有捕获整个屏幕。也许没有添加状态栏。这是我的直觉。
你当然可以尝试增加顶部边距并查看结果。
public void addHighlightCard(int x, int y, int width, int height) {
View highlightCard = inflater.inflate(R.layout.highlight_card, overlayRoot, false);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(width, height);
params.leftMargin = x;
params.topMargin = y+5;
highlightCard.setLayoutParams(params);
overlayRoot.addView(highlightCard, params);
}
分析
我发现您的代码有 4 个潜在问题。
屏幕坐标的使用
当您在此处创建亮点卡片时:
public void addHighlightCard(int x, int y, int width, int height) {
...
params.leftMargin = x;
params.topMargin = y;
...
}
你分配的是绝对坐标(屏幕坐标)x
和 y
而不是相对于你的 RelativeLayout
的坐标,这是错误的,因为 RelativeLayout
也有一些偏移关于设备屏幕。
要分配正确的坐标,请先计算 RelativeLayout
的屏幕坐标,然后根据这些坐标调整 x
和 y
。例如:
public void addHighlightCard(int x, int y, int width, int height) {
...
int[] screenCoordinates = new int[2];
overlayRoot.getLocationOnScreen(screenCoordinates);
int xOffset = screenCoordinates[0];
int yOffset = screenCoordinates[1];
params.leftMargin = x - xOffset;
params.topMargin = y - yOffset;
...
}
但是,如果你的根 View
占据了整个屏幕,应该没有问题。
RelativeLayout 的使用
我认为这可能是个问题,因为如果您想在另一个 FrameLayout
之上添加一个新的 View
,则应改用 FrameLayout
。但是,我不能确定这是否是一个问题,因为我没有看到完整的代码。
使用 MediaProjection 进行屏幕捕获
您没有向我们展示您是如何使用 MediaProjection 做到这一点的,所以这也可能是个问题。我使用了一种不同的方式来捕获您可以在下面看到的屏幕。
突出显示文本
您正在从 LayoutInflater
膨胀 View
以突出显示找到的文本。为了进行测试,我通过组合 ShapeDrawable
和 View
来做一些不同的事情,例如:
...
ShapeDrawable drawable = new ShapeDrawable();
drawable.getPaint().setColor(Color.YELLOW);
drawable.getPaint().setStyle(Paint.Style.STROKE);
drawable.getPaint().setStrokeWidth(5f);
View shapeView = new View(decorView.getContext());
shapeView.setBackground(drawable);
...
下面将提供完整的代码。
解决方案
既然你提到你的 RelativeLayout
占据了整个屏幕,我决定创建一个示例项目来证明与你的项目类似的项目运行良好。
下面是解释和相关代码。
build.gradle
plugins {
id 'com.android.application'
}
android {
compileSdkVersion 30
buildToolsVersion "30.0.2"
defaultConfig {
applicationId "com.example.myapplication"
minSdkVersion 24
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.3.0'
implementation 'com.google.android.gms:play-services-mlkit-text-recognition:16.1.3'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}
MainActivity.java
在这里,为了截图,我使用了以下代码:
Bitmap bitmap = Bitmap.createBitmap(decorView.getWidth(),
decorView.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
decorView.draw(canvas);
InputImage image = InputImage.fromBitmap(bitmap, 0);
我在 OnGlobalLayoutListener
中这样做是为了确保装饰视图具有适当的宽度和高度。好的,class 的完整代码如下:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RecyclerView recyclerView = findViewById(R.id.recycler_view);
recyclerView.setAdapter(new RecyclerViewAdapter(this));
recyclerView.setLayoutManager(new LinearLayoutManager(this));
View decorView = getWindow().getDecorView();
decorView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
decorView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
// take a screenshot of your screen
Bitmap bitmap = Bitmap.createBitmap(decorView.getWidth(),
decorView.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
decorView.draw(canvas);
InputImage image = InputImage.fromBitmap(bitmap, 0);
TextRecognizer recognizer = TextRecognition.getClient();
recognizer.process(image).addOnSuccessListener(new OnSuccessListener<Text>() {
@Override
public void onSuccess(Text text) {
for (Text.TextBlock textBlock : text.getTextBlocks()) {
if ("hello".equalsIgnoreCase(textBlock.getText())) {
Rect box = textBlock.getBoundingBox();
int left = box.left;
int top = box.top;
int right = box.right;
int bottom = box.bottom;
ShapeDrawable drawable = new ShapeDrawable();
drawable.getPaint().setColor(Color.YELLOW);
drawable.getPaint().setStyle(Paint.Style.STROKE);
drawable.getPaint().setStrokeWidth(5f);
View shapeView = new View(decorView.getContext());
shapeView.setBackground(drawable);
FrameLayout rootView = findViewById(R.id.root_view);
int[] location = new int[2];
rootView.getLocationOnScreen(location);
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(right - left,
bottom - top);
params.setMargins(left - location[0],
top - location[1],
right - location[0],
bottom - location[1]);
rootView.addView(shapeView, params);
}
}
}
});
}
});
}
private static class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.RecyclerViewHolder> {
private final Context context;
private final String[] elements = new String[] {"Hello", "Hello", "Bye", "Hello", "Hi there", "Hello"};
private RecyclerViewAdapter(Context context) {
this.context = context;
}
@NonNull
@Override
public RecyclerViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View item = LayoutInflater.from(context).
inflate(R.layout.list_item, parent, false);
return new RecyclerViewHolder(item);
}
@Override
public void onBindViewHolder(@NonNull RecyclerViewHolder holder, int position) {
holder.textView.setText(elements[position]);
}
@Override
public int getItemCount() {
return elements.length;
}
public static class RecyclerViewHolder extends RecyclerView.ViewHolder {
private final TextView textView;
public RecyclerViewHolder(@NonNull View itemView) {
super(itemView);
this.textView = itemView.findViewById(R.id.element_view);
}
}
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/root_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingStart="30dp"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical" />
</LinearLayout>
</FrameLayout>
如您所见,我使用 FrameLayout
作为根视图。
list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:orientation="vertical">
<TextView
android:id="@+id/element_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="16dp"
android:paddingBottom="8dp"
android:fontFamily="google-sans-medium"/>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#000"/>
</LinearLayout>
布局没有什么特别之处 - 只是 RecyclerView
的简单布局。
结果
所有 4 个“你好”结果都以黄色突出显示。
更新
如果不是从 Activity
获取显示大小,请确保使用正确的方法获取显示大小(在您的 GitHub 项目中,您是从 Service
) 因为你需要真实的显示尺寸,而不是其他东西。所以,请按以下步骤操作:
// get width and height
WindowManager wm = (WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
Point size = new Point();
display.getRealSize(size);
mWidth = size.x;
mHeight = size.y;
因此,在您的示例中,您必须将方法更改为:
private void createVirtualDisplay() {
// get width and height
WindowManager wm = (WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
Point size = new Point();
display.getRealSize(size);
mWidth = size.x;
mHeight = size.y;
...
}
就是这样。
我试图在 phone 屏幕上找到特定的词,然后在它们周围显示一个边界框(如果存在)。我遵循以下步骤:
- 捕获 整个 屏幕内容(使用 MediaProjection API)。
- 将此屏幕截图传递给 Google ML Kit 中的
- 检查检测到的单词,如果匹配则使用 ML Kit 返回的
Rect
在屏幕上绘制。
TextRecognizer
对象
它几乎 有效,这是在记事本应用程序上发现并突出显示单词 hello
的屏幕截图:
如您所见,半透明的黄色盒装不完全 hello
s。
这里是相关的代码示例。将屏幕截图位图传递给 ML Kit:
InputImage image = InputImage.fromBitmap(screenshotBitmap, 0);
//I checked: image, screen, and overlay view dimensions are exactly the same.
TextRecognizer recognizer = TextRecognition.getClient();
recognizer.process(image)
.addOnSuccessListener(this::processText);
processText
获取识别词的方法:
for (Text.Element element : getElements()) {
String elementText = element.getText();
Rect bounds = element.getBoundingBox(); //Getting the bounding box
if (elementText.equalsIgnoreCase("hello")) { //hello is hardcoded for now
addHighlightCard(bounds.left, bounds.top, bounds.width(), bounds.height());
}
}
最后是 addHighlightCard
,它创建并定位您在屏幕截图上看到的视图。它使用带有 RelativeLayout
的全屏覆盖,因为这种布局允许我指定子视图的确切位置和宽度。
public void addHighlightCard(int x, int y, int width, int height) {
View highlightCard = inflater.inflate(R.layout.highlight_card, overlayRoot, false);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(width, height);
params.leftMargin = x;
params.topMargin = y;
highlightCard.setLayoutParams(params);
overlayRoot.addView(highlightCard, params);
}
如您所见,我没有进行任何缩放,我捕获了整个屏幕,并且我使用了填满整个屏幕(甚至是工具栏)的布局。然后,我认为 ML Kit 返回的坐标应该可以直接用于绘制到屏幕上。但显然我错了,图像似乎在某处缩小了,但我不知道在哪里。
解决方案:事实证明,媒体投影 API 虚拟显示的大小不正确导致边界框未对齐。我不会让这个问题变得更长,而是 post a link here to a GitHub repository,在那里你可以找到一个示例应用程序,它显示使用媒体投影 API 并在屏幕截图上执行文本识别的工作方式。
示例应用程序:test-text-recognition
输入 MLKit 并显示在预览中的图像可能具有不同的尺寸。请参阅 mlkit example 了解如何缩放和映射它们。
它更像是下一个调试步骤而不是答案。: 所以你可以看到每个标记之间的边距随着每次高亮调用而增加。 看来您没有捕获整个屏幕。也许没有添加状态栏。这是我的直觉。
你当然可以尝试增加顶部边距并查看结果。
public void addHighlightCard(int x, int y, int width, int height) {
View highlightCard = inflater.inflate(R.layout.highlight_card, overlayRoot, false);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(width, height);
params.leftMargin = x;
params.topMargin = y+5;
highlightCard.setLayoutParams(params);
overlayRoot.addView(highlightCard, params);
}
分析
我发现您的代码有 4 个潜在问题。
屏幕坐标的使用
当您在此处创建亮点卡片时:
public void addHighlightCard(int x, int y, int width, int height) {
...
params.leftMargin = x;
params.topMargin = y;
...
}
你分配的是绝对坐标(屏幕坐标)x
和 y
而不是相对于你的 RelativeLayout
的坐标,这是错误的,因为 RelativeLayout
也有一些偏移关于设备屏幕。
要分配正确的坐标,请先计算 RelativeLayout
的屏幕坐标,然后根据这些坐标调整 x
和 y
。例如:
public void addHighlightCard(int x, int y, int width, int height) {
...
int[] screenCoordinates = new int[2];
overlayRoot.getLocationOnScreen(screenCoordinates);
int xOffset = screenCoordinates[0];
int yOffset = screenCoordinates[1];
params.leftMargin = x - xOffset;
params.topMargin = y - yOffset;
...
}
但是,如果你的根 View
占据了整个屏幕,应该没有问题。
RelativeLayout 的使用
我认为这可能是个问题,因为如果您想在另一个 FrameLayout
之上添加一个新的 View
,则应改用 FrameLayout
。但是,我不能确定这是否是一个问题,因为我没有看到完整的代码。
使用 MediaProjection 进行屏幕捕获
您没有向我们展示您是如何使用 MediaProjection 做到这一点的,所以这也可能是个问题。我使用了一种不同的方式来捕获您可以在下面看到的屏幕。
突出显示文本
您正在从 LayoutInflater
膨胀 View
以突出显示找到的文本。为了进行测试,我通过组合 ShapeDrawable
和 View
来做一些不同的事情,例如:
...
ShapeDrawable drawable = new ShapeDrawable();
drawable.getPaint().setColor(Color.YELLOW);
drawable.getPaint().setStyle(Paint.Style.STROKE);
drawable.getPaint().setStrokeWidth(5f);
View shapeView = new View(decorView.getContext());
shapeView.setBackground(drawable);
...
下面将提供完整的代码。
解决方案
既然你提到你的 RelativeLayout
占据了整个屏幕,我决定创建一个示例项目来证明与你的项目类似的项目运行良好。
下面是解释和相关代码。
build.gradle
plugins {
id 'com.android.application'
}
android {
compileSdkVersion 30
buildToolsVersion "30.0.2"
defaultConfig {
applicationId "com.example.myapplication"
minSdkVersion 24
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.3.0'
implementation 'com.google.android.gms:play-services-mlkit-text-recognition:16.1.3'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}
MainActivity.java
在这里,为了截图,我使用了以下代码:
Bitmap bitmap = Bitmap.createBitmap(decorView.getWidth(),
decorView.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
decorView.draw(canvas);
InputImage image = InputImage.fromBitmap(bitmap, 0);
我在 OnGlobalLayoutListener
中这样做是为了确保装饰视图具有适当的宽度和高度。好的,class 的完整代码如下:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RecyclerView recyclerView = findViewById(R.id.recycler_view);
recyclerView.setAdapter(new RecyclerViewAdapter(this));
recyclerView.setLayoutManager(new LinearLayoutManager(this));
View decorView = getWindow().getDecorView();
decorView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
decorView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
// take a screenshot of your screen
Bitmap bitmap = Bitmap.createBitmap(decorView.getWidth(),
decorView.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
decorView.draw(canvas);
InputImage image = InputImage.fromBitmap(bitmap, 0);
TextRecognizer recognizer = TextRecognition.getClient();
recognizer.process(image).addOnSuccessListener(new OnSuccessListener<Text>() {
@Override
public void onSuccess(Text text) {
for (Text.TextBlock textBlock : text.getTextBlocks()) {
if ("hello".equalsIgnoreCase(textBlock.getText())) {
Rect box = textBlock.getBoundingBox();
int left = box.left;
int top = box.top;
int right = box.right;
int bottom = box.bottom;
ShapeDrawable drawable = new ShapeDrawable();
drawable.getPaint().setColor(Color.YELLOW);
drawable.getPaint().setStyle(Paint.Style.STROKE);
drawable.getPaint().setStrokeWidth(5f);
View shapeView = new View(decorView.getContext());
shapeView.setBackground(drawable);
FrameLayout rootView = findViewById(R.id.root_view);
int[] location = new int[2];
rootView.getLocationOnScreen(location);
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(right - left,
bottom - top);
params.setMargins(left - location[0],
top - location[1],
right - location[0],
bottom - location[1]);
rootView.addView(shapeView, params);
}
}
}
});
}
});
}
private static class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.RecyclerViewHolder> {
private final Context context;
private final String[] elements = new String[] {"Hello", "Hello", "Bye", "Hello", "Hi there", "Hello"};
private RecyclerViewAdapter(Context context) {
this.context = context;
}
@NonNull
@Override
public RecyclerViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View item = LayoutInflater.from(context).
inflate(R.layout.list_item, parent, false);
return new RecyclerViewHolder(item);
}
@Override
public void onBindViewHolder(@NonNull RecyclerViewHolder holder, int position) {
holder.textView.setText(elements[position]);
}
@Override
public int getItemCount() {
return elements.length;
}
public static class RecyclerViewHolder extends RecyclerView.ViewHolder {
private final TextView textView;
public RecyclerViewHolder(@NonNull View itemView) {
super(itemView);
this.textView = itemView.findViewById(R.id.element_view);
}
}
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/root_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingStart="30dp"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical" />
</LinearLayout>
</FrameLayout>
如您所见,我使用 FrameLayout
作为根视图。
list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:orientation="vertical">
<TextView
android:id="@+id/element_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="16dp"
android:paddingBottom="8dp"
android:fontFamily="google-sans-medium"/>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#000"/>
</LinearLayout>
布局没有什么特别之处 - 只是 RecyclerView
的简单布局。
结果
所有 4 个“你好”结果都以黄色突出显示。
更新
如果不是从 Activity
获取显示大小,请确保使用正确的方法获取显示大小(在您的 GitHub 项目中,您是从 Service
) 因为你需要真实的显示尺寸,而不是其他东西。所以,请按以下步骤操作:
// get width and height
WindowManager wm = (WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
Point size = new Point();
display.getRealSize(size);
mWidth = size.x;
mHeight = size.y;
因此,在您的示例中,您必须将方法更改为:
private void createVirtualDisplay() {
// get width and height
WindowManager wm = (WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
Point size = new Point();
display.getRealSize(size);
mWidth = size.x;
mHeight = size.y;
...
}
就是这样。