1.效果如下图
说明:该例子是我最近学习android时的一个小例子,目前再看的android教程为尚硅谷的入门教程。链接:https://www.gulixueyuan.com/course/124/task/1893/show
从截图效果来看有一个有些卡顿,但是实际操作的时候并看不出来两者有任何区别,都比较流畅。下面只介绍Qt for android的流程。如果想要android 源码和图片资源可以联系我QQ:1024847801。
2.Qt QML 开发移动端界面关于宽高的定义
为了达到和android原生一样的效果,我一直再寻找如何让qml写出的界面和android的一样。搜了好多相关文章,试了好多,最后发现直接写宽高就行。不需要做任何转换。
android中xml的尺寸:
<RelativeLayout
android:id="@+id/layout_level3"
android:layout_width="280dp"
android:layout_height="140dp"
android:layout_centerHorizontal="true"
android:layout_alignParentBottom="true"
android:background="@drawable/level3" >
Qt中,QML与之相对应的控件宽高
Item {
id: level3Item
width: 280 ; //和android的值一样
height: 140 ;//和android的值一样
anchors.horizontalCenter: parent.horizontalCenter;
anchors.bottom: parent.bottom;
state: "NORMAL3"
}
这是最外层那个大半圆环的背景图片。宽和高与android保持一样即可。
3.qt 书写动画的代码与android比较
在android中,添加动画的代码为:
public static void hideView(ViewGroup view , int startOffset) {
//利用属性动画解决
view.setPivotX(view.getWidth()/2);
view.setPivotY(view.getHeight());
ObjectAnimator animatorRotate =ObjectAnimator.ofFloat(view , "rotation" , 0 , 180);
animatorRotate.setDuration(500);
animatorRotate.setStartDelay(startOffset);
animatorRotate.start();
}
public static void showView(ViewGroup view , int startOffset) {
view.setPivotX(view.getWidth()/2);
view.setPivotY(view.getHeight());
ObjectAnimator animatorRotate =ObjectAnimator.ofFloat(view , "rotation" , 180 , 360);
animatorRotate.setDuration(500);
animatorRotate.setStartDelay(startOffset);
animatorRotate.start();
}
Qt中的代码为:
states: [State {
name: "NORMAL3"
PropertyChanges { target: level3Item; rotation: 360; transformOrigin: Item.Bottom }
},
State {
name: "ROTATED3";
PropertyChanges { target: level3Item; rotation:180 ; transformOrigin: Item.Bottom }
}
]
transitions: Transition {
SequentialAnimation {
PauseAnimation { duration: isShowLevel2 ? 100 : (isShowLevel1 ? 80 : 200) }
RotationAnimation { duration: 500; direction: RotationAnimation.Clockwise }
}
}
//状态切换响应:
if( isShowLevel3 ) {
level3Item.state = "ROTATED3"
isShowLevel3 = false;
} else {
level3Item.state = "NORMAL3"
isShowLevel3 = true;
}
}
4. Qt 捕获android下的Back按键并传给Qt
android原生代码:
public boolean onKeyDown(int keyCode, KeyEvent event) {
if( keyCode == KeyEvent.KEYCODE_BACK ) {
//如果一级、二级、三级菜单显示,就隐藏
if ( isShowLevel1 ) {
isShowLevel1 = false;
Tools.hideView(level1);
if ( isShowLevel2 ) {
isShowLevel2 = false;
Tools.hideView(level2 , 200);
}
if ( isShowLevel3 ) {
isShowLevel3 = false;
Tools.hideView(level3 , 400);
}
} else {
//如果一级二级隐藏就显示
isShowLevel1 = true;
Tools.showView(level1);
if ( !isShowLevel2 ) {
isShowLevel2 = true;
Tools.showView(level2 , 200);
}
}
return true;
}
return super.onKeyDown(keyCode, event);
}
Qt捕获android下back按键的代码:通过jni注册回调来实现。
4.1 首先写一个java类
package com.lsy.youku_menu;
import org.qtproject.qt5.android.bindings.QtApplication;
//tommego
import java.util.List;
import android.content.Context;
import android.util.Log;
import android.view.KeyEvent;
//屏幕像素密度
import android.util.DisplayMetrics;
//蓝牙 开关
import android.bluetooth.BluetoothAdapter;
public class Screen extends org.qtproject.qt5.android.bindings.QtActivity{
private static Screen m_instance;//单例对象
private static BluetoothAdapter m_bluetoothAdapter;//蓝牙控制对象
public Screen(){
m_instance = this;//实例化单例对象
m_bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (!m_bluetoothAdapter.isEnabled()){
//打开蓝牙
m_bluetoothAdapter.enable();
}
}
//获取屏幕像素密度
public static double getDentisy(){
DisplayMetrics metrics=new DisplayMetrics();
m_instance.getWindowManager().getDefaultDisplay().getMetrics(metrics);
return metrics.density;
// return 3;
}
//按键捕获只需要关注下面,上面是测试像素密度的。
public native void OnESCdown();
@Override
public boolean onKeyDown(int keyCode, KeyEvent event)
{
int newKeyCode = keyCode;
if ( (keyCode == KeyEvent.KEYCODE_BACK) )
{
OnESCdown();//通知<调用>C++层的 onEscDown函数
newKeyCode = KeyEvent.KEYCODE_MEDIA_PREVIOUS;
}
if (QtApplication.m_delegateObject != null && QtApplication.onKeyDown != null) {
Log.e("TAG" , "back.................newKeyCode"+newKeyCode);
return (Boolean)QtApplication.invokeDelegateMethod(QtApplication.onKeyDown, newKeyCode,event);
}
else
return super.onKeyDown(newKeyCode, event);
}
}
4.2.写一个C++类,并注册函数OnESCdown。
void DeviceScreen::RegJni()
{
static const JNINativeMethod methods[] {
{ "OnESCdown", "()V", (void*)onEscDown }
};
qDebug()<<"RegisterNatives 0***********";
qDebug()<<"RegisterNatives 1***********";
const char* kClassName ="com/lsy/youku_menu/Screen";
QtAndroid::runOnAndroidThreadSync([=](){
QAndroidJniEnvironment qtEnv;
jclass clazz;
JNIEnv *EV = qtEnv.operator ->();
clazz =EV->FindClass(kClassName);
if (clazz == NULL)
{
qDebug()<<"erro clazz";
return ;
}
qDebug()<<"RegisterNatives 2***********";
if (EV->RegisterNatives(clazz, methods, sizeof(methods) / sizeof(methods[0])) != JNI_OK)
{
printf("register native method failed!\n");
return;
}else{
qDebug()<<"RegisterNatives 搞定***********";
}
});
//这里可以找到要注册的类,前提是这个类已经加载到java虚拟机中。 这里说明,动态库和有native方法的类之间,没有任何对应关系。
// QAndroidJniObject javaClass(kClassName);
// clazz = env->GetObjectClass(javaClass.object());
// qDebug() <<"find ExtendsQtNative"<< clazz;
//EV->DeleteLocalRef(clazz);//删除引用避免内存泄漏
qDebug()<<"RegisterNatives OK***********";
}
void DeviceScreen::onEscDown()
{
qDebug()<<"C++ onEscDown";
device->emitSignal();
}
然后在运行的时候按下back按键,就会调用onEscDown()函数,然后再通知到QML
Connections{
target: mydevice;
function onOnBackKeyChanged() {
//如果1、2、3都显示,则全部隐藏
if ( isShowLevel1 ) {
level1Item.state = "ROTATED"
isShowLevel1 = false;
if( isShowLevel2 ) {
level2Item.state = "ROTATED2"
isShowLevel2 = false;
}
if ( isShowLevel3 ) {
level3Item.state = "ROTATED3"
isShowLevel3 = false;
}
} else {
level1Item.state = "NORMAL"
isShowLevel1 = true;
level2Item.state = "NORMAL2"
isShowLevel2 = true;
}
}
}
运行效果请看开头,打印如下:
由于时间原因写的比较仓促。
经过比较,Qt开发android还是需要懂一些android开发。最好系统的学习下android开发然后再学习JNI相关知识。Qt for android 路还是很长的。