UE UPL的使用

UPL调研

基本思路:
使用UPL中的xml语言,使用他来介入安卓的打包过程,将我们需要的功能添加到UE工程中
UPL
定义:UPL 全称 Unreal Plugin Language,主要的作用是参加ue的打包过程
理解:UPL对应的就是安卓工程中的activty_main.xml(找个文件的补充),它规定了安卓包构建的方式和顺序

所以我们在开发的过程中,可以通过UPL在Gameactivty增加我们写的java代码来保证使用C++来在安卓的环境下来使用java的代码

<!-- 导入对象到 GameActivity.java -->
<gameActivityImportAdditions>
    <insert>
      import android.net.Uri;
      import android.net.ConnectivityManager;
    </insert>
</gameActivityImportAdditions>
<!-- 导入 java方法到 GameActivity.java -->
<gameActivityClassAdditions> </gameActivityClassAdditions>
<!-- 增加 代码到 OnCreateAdditions函数 -->
<gameActivityOnCreateAdditions></gameActivityOnCreateAdditions>

通过这些方法就可以将代码增加到Gameactivity.java,这些具体的定义可以参考
UnrealBuildTool/System/UnrealPluginLanguage.csUE的源码中的注释
Java函数签名
JNI:JNI全称Java Native Interface,及java原生接口,主要用于jave调用其他语言代码,其他语言调用java代码。
我们通过C++ 去java的方法,通常是使用函数签名来获取Java中的函数,函数签名定义了函数的名称,返回值和参数类型

public int AndroidThunkJava_GetCurCpu()
{
      int result = 0;
      FileReader fr = null;
      BufferedReader br = null;
 }

比如上面的这个java函数,他的函数签名就为 () I;,其中括号中为参数列表,I表示返回值为int
他的签名计算是有一个规则,可以参考下表

  • String:Ljava/lang/String;
  • Object:Ljava/lang/Object;
    以上两条为补充内容,他不在基本类型中,所以为特殊表示
    public string AndroidThunkJava_GetCurCpu(int val)
    //他的函数签名为 (I)Ljava/lang/String;

JNI:Java to C++

UE 给我们的游戏生成的 GameActivity 中也声明了很多的 native 函数,这些函数是在 C++ 实现的,在 Java 中执行到这些函数会自动调用到引擎的 C++ 代码中:

public native int nativeGetCPUFamily();
public native boolean nativeSupportsNEON();
public native void nativeSetAffinityInfo(boolean bEnableAffinity, int bigCoreMask, int littleCoreMask);
public native void nativeSetConfigRulesVariables(String[] KeyValuePairs);
public native boolean nativeIsShippingBuild();
public native void nativeSetAndroidStartupState(boolean bDebuggerAttached);
public native void nativeSetGlobalActivity(boolean bUseExternalFilesDir, boolean bPublicLogFiles, String internalFilePath, String externalFilePath, boolean bOBBInAPK, String APKPath);
public native void nativeSetObbFilePaths(String OBBMainFilePath, String OBBPatchFilePath);
public native void nativeSetWindowInfo(boolean bIsPortrait, int DepthBufferPreference);
public native void nativeSetObbInfo(String ProjectName, String PackageName, int Version, int PatchVersion, String AppType);
public native void nativeSetAndroidVersionInformation( String AndroidVersion, String PhoneMake, String PhoneModel, String PhoneBuildNumber, String OSLanguage );
public native void nativeSetSurfaceViewInfo(int width, int height);
public native void nativeSetSafezoneInfo(boolean bIsPortrait, float left, float top, float right, float bottom);
public native void nativeConsoleCommand(String commandString);
public native void nativeVirtualKeyboardChanged(String contents);
public native void nativeVirtualKeyboardResult(boolean update, String contents);
public native void nativeVirtualKeyboardSendKey(int keyCode);
public native void nativeVirtualKeyboardSendTextSelection(String contents, int selStart, int selEnd);
public native void nativeVirtualKeyboardSendSelection(int selStart, int selEnd);
public native void nativeInitHMDs();
public native void nativeResumeMainInit();
public native void nativeOnActivityResult(GameActivity activity, int requestCode, int resultCode, Intent data);
public native void nativeGoogleClientConnectCompleted(boolean bSuccess, String accessToken);
public native void nativeVirtualKeyboardShown(int left, int top, int right, int bottom);
public native void nativeVirtualKeyboardVisible(boolean bShown);
public native void nativeOnConfigurationChanged(boolean bPortrait);
public native void nativeOnInitialDownloadStarted();
public native void nativeOnInitialDownloadCompleted();
public native void nativeHandleSensorEvents(float[] tilt, float[] rotation_rate, float[] gravity, float[] acceleration);

简单例子
在java中声明一个简单函数

<gameActivityClassAdditions>
  <insert>
    public native void nativeDoTester(String Msg);
  </insert>
</gameActivityClassAdditions>

在C++代码的任何位置去实现这个代码就好

#if PLATFORM_ANDROID
JNI_METHOD void Java_com_epicgames_unreal_GameActivity_nativeDoTester
(JNIEnv* jenv, jobject thiz, jstring msg)
{
        if (JNIEnv* Env = FAndroidApplication::GetJavaEnv())
        {
                FString FinalResult = FJavaHelper::FStringFromLocalRef(Env, msg);
                MyFunc::Name = FinalResult;
        }
}

com.epicgames_unreal 是 UE 生成的 GameActivity.java 的包名 (package com.epicgames_unreal)。
可以看到,在 C++ 中实现 JNIMETHOD 的函数名是以下规则:.
RType Java_PACKAGENAME_CLASSNAME_FUNCNAME(JNIEnv*,jobject thiz,Oher…)

JNI:C++ to Java
通过上面的转化,我们知道了怎么通过函数签名来找到java代码,我们在UE中就可以通过函数名和函数签名来在C++中调用java
其中通常用到的为下面三个头文件

// Runtime/Launch/Public/Android
#include "Android/AndroidJNI.h"
// Runtime/Core/Public/Android
#include "Android/AndroidJavaEnv.h"
// Runtime/Core/Public/Android
#include "Android/AndroidJava.h"

想要在 UE 中调用到它,首先要获取它的 jmethodID,需要通过函数所属的类、函数名字,签名三种信息来获取:

if (JNIEnv* Env = FAndroidApplication::GetJavaEnv())
{
      //通过FindMethod来获取他的jmethodID ,其中需要填类名,函数名字,签名
      static jmethodID Method = FJavaWrapper::FindMethod(Env, FJavaWrapper::GameActivityClassID, "AndroidThunkJava_GetCurCpu", "()I", false);
      //找到jmethodID 后,通过CallIntMethod就可以调用到函数
      jint Result = FJavaWrapper::CallIntMethod(Env, FJavaWrapper::GameActivityThis, Method);
      return Result;
}

注:当返回值为string时还需要转化为UE的字符串才可以使用
FString FinalResult = FJavaHelperEx::FStringFromLocalRef(Env,JstringResult);

遇到的问题
  1. 找不到头文件 #include “Android/AndroidJNI.h”
    解决:需要在bulid.cs中添加
if (Target.Platform == UnrealTargetPlatform.Android)
        {
            PrivateDependencyModuleNames.AddRange(new string[] { "Launch", "AndroidPermission" });
            string PluginPath = Utils.MakePathRelativeTo(ModuleDirectory, Target.RelativeEnginePath);
            AdditionalPropertiesForReceipt.Add("AndroidPlugin", Path.Combine(PluginPath, "Android_clik_APL.xml"));
        }
  1. 直接在VS平台编译JNI代码无法通过编译
    解决:需要在编译时增加预处理宏,保证在安卓平台时才可以运行
    #if PLATFORM_ANDROID
    #endif

  2. 导入对象出现错误
    解决:
    如果有如下错误:提示下面两个包找不到
    import android.support.v4.app.ActivityCompat;
    import android.support.v7.app.AppCompatActivity;
    可以改为:
    import androidx.core.app.ActivityCompat;
    import androidx.appcompat.app.AppCompatActivity;

这里库找不到可以替换为

import android.support.annotation.
替换为
import androidx.annotation;


4. 示例代码的修改,以下代码的删除,这里会导致软件闪退
<gameActivityOnPauseAdditions>
 <insert>
 android.util.Log.d("UE4", "onPause");
 nativeUELog("onPause");
 </insert>
 </gameActivityOnPauseAdditions>
<gameActivityOnResumeAdditions>
  <insert>
    android.util.Log.d("UE4", "onResume");
    nativeUELog("onResume");
  </insert>
</gameActivityOnResumeAdditions>

功能实现效果

通过UMG在屏幕生成按钮,点击按钮后触发事件,调用JAVA函数,获取到CPU数值,并显示在屏幕右上角

java中native方法

定义:他的作用就是一个java调用非java接口的方法,他只是在java中声明,而具体的实现是在C语言中实现,其实很类似于C++中的extern c,是编译器的一种方法
public class IHaveNatives
{
native public void Native1( int x ) ;
native static public long Native2() ;
native synchronized private float Native3( Object o ) ;
native void Native4( int[] ary ) throws Exception ;
}

举例是这样的,native可以与其他的java标识符连用,他的暗示是这些函数是有实现的
扩展:GameActivity.java文件的学习

1.1 安卓项目文件结构

activity定义:用户看得见摸的着的是手机屏幕。我们要在手机屏幕上显示文字图像等信息,并对用户的点击滑动等等操作作出不同的反应。 App中与用户交互的大任由Activity来承担。当用户手指点击手机屏幕时,Android系统检测到屏幕发生的事情,将这一事件分发对应的App处理。这里要注意,activity接收到的是系统给的信息。系统会判断这些交互消息该给哪个app来处理。

入口函数:public class GameActivity extends NativeActivity
在gameActivity他的入口类为GameActivity ,是继承自NativeActivity,也就是他的页面
生命周期:

  1. Oncreate 布局的初始化
  2. Onstart 启动的时候的状态
  3. Onresume 渲染的完成
  4. Onpause 暂停后去停止
  5. Onstop 回到桌面
  6. Onresart 重新开始

同时在UE的UPL中也提供相应的接口供我们使用,在其中添加代码,下面是一些举例

* <!-- optional additions to GameActivity onCreate in GameActivity.java -->
     *  <gameActivityOnCreateAdditions> </gameActivityOnCreateAdditions>
     *  
     *  <!-- optional additions to GameActivity onDestroy in GameActivity.java -->
     *  <gameActivityOnDestroyAdditions> </gameActivityOnDestroyAdditions>
     *  
     *  <!-- optional additions to GameActivity onConfigurationChanged in GameActivity.java -->
     *  <gameActivityonConfigurationChangedAdditions> </gameActivityonConfigurationChangedAdditions>
     *  
     *  <!-- optional additions to GameActivity onStart in GameActivity.java -->
     *  <gameActivityOnStartAdditions> </gameActivityOnStartAdditions>
     *  
     *  <!-- optional additions to GameActivity onStop in GameActivity.java -->
     *  <gameActivityOnStopAdditions> </gameActivityOnStopAdditions>
     *  
     *  <!-- optional additions to GameActivity onPause in GameActivity.java -->
     *  <gameActivityOnPauseAdditions> </gameActivityOnPauseAdditions>
     *  
     *  <!-- optional additions to GameActivity onResume in GameActivity.java -->
     *  <gameActivityOnResumeAdditions> </gameActivityOnResumeAdditions>
     *  
2.1 Activity 启动,携带参数启动

通常activity离不开intent这个类,通常把信息包含在intent对象中,然后执行启动,常用的方法是 startActivity (Intent intent) ,我们在activity中通信的过程中的信息就可以听过intent这个类去传递
//实现一个简单的跳转
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

Intent intent = new Intent(getApplicationContext(),myActivity2.class);
intent.putExtra("13",100);
startActivities(new Intent[]{intent});

}

2.2 任务,返回栈

任务指的是在执行特定作业的时候与用户交互的一系列Activity,这些Activity按照各组打开顺序排列在堆栈中.

2.3 Avtivity的四种模式
2.4 Service综述

service是一种在后台执行,长时间运行操作而不提供界面的应用组件,服务可由其他组件启动,就是用户在切换到其他应用的时候,服务仍在后台运行

2.4.1 前台,后台服务与绑定

前台:前台服务执行一些用户能注意到的操作。例如,音频应用会使用前台服务来播放音频曲目。前台服务必 须显示通知。 即使用户停止与应用的交互,前台服务仍会继续运行
后台:后台服务执行用户不会直接注意到的操作。例如,如果应用使用某个服务来压缩其存储空间,则此服务通常是后台服务。
绑定服务:当应用组件通过调用 bindService() 绑定到服务时,服务即处于绑定状态。 绑定服务会提供客户端-服务器接口,以便组件与服务进行交互、发送请求、接收结果,甚至是利用进程间通信 (IPC) 跨进程执行,这些操作仅当与另一个应用组件绑定时,绑定服务才会运行。多个组件可同时绑定到该服务,但全部取消绑定后,该服务即会被销毁。

2.4.2 启动服务

startService(Intent(applicationContext, ServiceStartDemo::class.java))

调用方法后,ServiceStartDemo服务会启动起来。 首次启动的话,服务会走 onCreate 和
onStartCommand 方法。 初始化性质的代码,放在 onCreate 。

2.4.3 停止前台服务

在Service中调用 stopForeground(boolean) 方法,能停止前台,但是不退出整个服务。 这个boolean
表示是否取消掉前台服务的通知。false表示保留通知。

3.1 Fragment基础概念

定义:Fragment直译为碎片,Fragment表示FragmentActivity中行为或界面的一部分,你可以在Activity中组合多个片段,从而构建多窗格界面,其实可以理解为一个子Activity,但是片段必须始终托管在Activity中,他的生命周期也直接受Activity的影响
Fragment的优点

  1. Fragment加载灵活,替换方便。定制你的UI,在不同尺寸的屏幕上创建合适的UI,提高用户体
    验。
  2. 可复用,页面布局可以使用多个Fragment,不同的控件和内容可以分布在不同的Fragment上。
  3. 使用Fragment,可以少用一些Activity。一个Activity可以管辖多个Fragment。
    3.2 fragment的生命周期

使用DialogFragment来完成一个弹窗的功能

  1. 在onCreate方法中接收传入的数据。传递数据使用了Bundle
  2. 在onCreateView方法中,使用上文建立的layout
  • 在onViewCreated方法中进行ui操作
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;
public class SimpleDialog extends DialogFragment {
    public static final String K_TITLE = "k_title"; // 传输数据时用到的key
    public static final String K_CONTENT = "k_content";

    private String title;
    private String content;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Bundle in = getArguments();
        if (in != null) {
            title = in.getString(K_TITLE);
            content = in.getString(K_CONTENT);
        }
    }

    //作用是将片段布局插入到父级viewGrroup中 
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.dialog_simple, container, false);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        TextView titleTv = view.findViewById(R.id.title_tv);
        TextView contentTv = view.findViewById(R.id.content_tv);

        titleTv.setText(title);
        contentTv.setText(content);
    }
}

定义的layout文件如下

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="12dp">

    <TextView
        android:id="@+id/title_tv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:textColor="#111111"
        android:textSize="16sp"
        android:textStyle="bold"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/content_tv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:gravity="center"
        android:textColor="#111111"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/title_tv" />

</androidx.constraintlayout.widget.ConstraintLayout>

最后在activity中调用

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    popSimpleDialog1("123", "test");
}
private void popSimpleDialog1(String title, String content) {
    SimpleDialog dialog = new SimpleDialog();
    Bundle bundle = new Bundle();
    bundle.putString(SimpleDialog.K_TITLE, title);
    bundle.putString(SimpleDialog.K_CONTENT, content);
    dialog.setArguments(bundle);
    //getSupportFragmentManager为获取FragmentTransaction实例
    dialog.show(getSupportFragmentManager(), "one-tag");
}

}

最终实现的效果如下图所示
在这里插入图片描述

java中native方法

定义:他的作用就是一个java调用非java接口的方法,他只是在java中声明,而具体的实现是在C语言中实现,其实很类似于C++中的extern c,是编译器的一种方法

   public class IHaveNatives
    {
      native public void Native1( int x ) ;
      native static public long Native2() ;
      native synchronized private float Native3( Object o ) ;
      native void Native4( int[] ary ) throws Exception ;
    } 
扩展:GameActivity.java文件的学习
1.1 安卓项目文件结构

在这里插入图片描述
activity定义:用户看得见摸的着的是手机屏幕。我们要在手机屏幕上显示文字图像等信息,并对用户的点击滑动等等操作作出不同的反应。 App中与用户交互的大任由Activity来承担。当用户手指点击手机屏幕时,Android系统检测到屏幕发生的事情,将这一事件分发对应的App处理。这里要注意,activity接收到的是系统给的信息。系统会判断这些交互消息该给哪个app来处理。

入口函数:public class GameActivity extends NativeActivity
在gameActivity他的入口类为GameActivity ,是继承自NativeActivity,也就是他的页面

生命周期:
  1. Oncreate 布局的初始化
  2. Onstart 启动的时候的状态
  3. Onresume 渲染的完成
  4. Onpause 暂停后去停止
  5. Onstop 回到桌面
  6. Onresart 重新开始

在这里插入图片描述
同时在UE的UPL中也提供相应的接口供我们使用,在其中添加代码,下面是一些举例

* <!-- optional additions to GameActivity onCreate in GameActivity.java -->
     *  <gameActivityOnCreateAdditions> </gameActivityOnCreateAdditions>
     *  
     *  <!-- optional additions to GameActivity onDestroy in GameActivity.java -->
     *  <gameActivityOnDestroyAdditions> </gameActivityOnDestroyAdditions>
     *  
     *  <!-- optional additions to GameActivity onConfigurationChanged in GameActivity.java -->
     *  <gameActivityonConfigurationChangedAdditions> </gameActivityonConfigurationChangedAdditions>
     *  
     *  <!-- optional additions to GameActivity onStart in GameActivity.java -->
     *  <gameActivityOnStartAdditions> </gameActivityOnStartAdditions>
     *  
     *  <!-- optional additions to GameActivity onStop in GameActivity.java -->
     *  <gameActivityOnStopAdditions> </gameActivityOnStopAdditions>
     *  
     *  <!-- optional additions to GameActivity onPause in GameActivity.java -->
     *  <gameActivityOnPauseAdditions> </gameActivityOnPauseAdditions>
     *  
     *  <!-- optional additions to GameActivity onResume in GameActivity.java -->
     *  <gameActivityOnResumeAdditions> </gameActivityOnResumeAdditions>
     *  

2.1 Activity 启动,携带参数启动

通常activity离不开intent这个类,通常把信息包含在intent对象中,然后执行启动,常用的方法是 startActivity (Intent intent) ,我们在activity中通信的过程中的信息就可以听过intent这个类去传递

//实现一个简单的跳转
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Intent intent = new Intent(getApplicationContext(),myActivity2.class);
    intent.putExtra("wdf",100);
    startActivities(new Intent[]{intent});
}

2.2 任务,返回栈

任务指的是在执行特定作业的时候与用户交互的一系列Activity,这些Activity按照各组打开顺序排列在堆栈中.

2.3 Avtivity的四种模式

在这里插入图片描述

2.4 Service综述

service是一种在后台执行,长时间运行操作而不提供界面的应用组件,服务可由其他组件启动,就是用户在切换到其他应用的时候,服务仍在后台运行

2.4.1 前台,后台服务与绑定

**前台:**前台服务执行一些用户能注意到的操作。例如,音频应用会使用前台服务来播放音频曲目。前台服务必 须显示通知。 即使用户停止与应用的交互,前台服务仍会继续运行
**后台:**后台服务执行用户不会直接注意到的操作。例如,如果应用使用某个服务来压缩其存储空间,则此服务通常是后台服务。
**绑定服务:**当应用组件通过调用 bindService() 绑定到服务时,服务即处于绑定状态。 绑定服务会提供客户端-服务器接口,以便组件与服务进行交互、发送请求、接收结果,甚至是利用进程间通信 (IPC) 跨进程执行,这些操作仅当与另一个应用组件绑定时,绑定服务才会运行。多个组件可同时绑定到该服务,但全部取消绑定后,该服务即会被销毁。

2.4.2 启动服务
 startService(Intent(applicationContext, ServiceStartDemo::class.java))

调用方法后,ServiceStartDemo服务会启动起来。 首次启动的话,服务会走 onCreate 和
onStartCommand 方法。 初始化性质的代码,放在 onCreate 。

2.4.3 停止前台服务

在Service中调用 stopForeground(boolean) 方法,能停止前台,但是不退出整个服务。 这个boolean
表示是否取消掉前台服务的通知。false表示保留通知。

3.1 Fragment基础概念

定义:Fragment直译为碎片,Fragment表示FragmentActivity中行为或界面的一部分,你可以在Activity中组合多个片段,从而构建多窗格界面,其实可以理解为一个子Activity,但是片段必须始终托管在Activity中,他的生命周期也直接受Activity的影响
Fragment的优点

  1. Fragment加载灵活,替换方便。定制你的UI,在不同尺寸的屏幕上创建合适的UI,提高用户体
    验。
  2. 可复用,页面布局可以使用多个Fragment,不同的控件和内容可以分布在不同的Fragment上。
  3. 使用Fragment,可以少用一些Activity。一个Activity可以管辖多个Fragment。
    3.2 fragment的生命周期
    在这里插入图片描述
使用DialogFragment来完成一个弹窗的功能
  1. 在onCreate方法中接收传入的数据。传递数据使用了Bundle
  2. 在onCreateView方法中,使用上文建立的layout
  • 在onViewCreated方法中进行ui操作
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;

public class SimpleDialog extends DialogFragment {
    public static final String K_TITLE = "k_title"; // 传输数据时用到的key
    public static final String K_CONTENT = "k_content";

    private String title;
    private String content;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Bundle in = getArguments();
        if (in != null) {
            title = in.getString(K_TITLE);
            content = in.getString(K_CONTENT);
        }
    }

    //作用是将片段布局插入到父级viewGrroup中 
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.dialog_simple, container, false);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        TextView titleTv = view.findViewById(R.id.title_tv);
        TextView contentTv = view.findViewById(R.id.content_tv);

        titleTv.setText(title);
        contentTv.setText(content);
    }
}

定义的layout文件如下

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="12dp">

    <TextView
        android:id="@+id/title_tv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:textColor="#111111"
        android:textSize="16sp"
        android:textStyle="bold"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/content_tv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:gravity="center"
        android:textColor="#111111"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/title_tv" />

</androidx.constraintlayout.widget.ConstraintLayout>
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        popSimpleDialog1("王东方", "test");
    }
    private void popSimpleDialog1(String title, String content) {
        SimpleDialog dialog = new SimpleDialog();
        Bundle bundle = new Bundle();
        bundle.putString(SimpleDialog.K_TITLE, title);
        bundle.putString(SimpleDialog.K_CONTENT, content);
        dialog.setArguments(bundle);
        //getSupportFragmentManager为获取FragmentTransaction实例
        dialog.show(getSupportFragmentManager(), "one-tag");
    }
}

最终实现的效果如下图所示
在这里插入图片描述

res应用资源

资源分类的视图如下把资源放进对应的目录后,可使用在项目 R 类中生成的资源ID来访问这些资源。形如 R.drawable.icon , R.layout.main_activity 。 R 类是自动生成的。代表resources。

在这里插入图片描述

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值