月度归档: 2021 年 5 月

Android 个人中心界面实现

效果展示


依赖说明:
implementation ‘com.github.bumptech.glide:glide:3.7.0’

implementation ‘jp.wasabeef:glide-transformations:2.0.1’

implementation ‘de.hdodenhof:circleimageview:2.1.0’

glide框架:用于加载图片
glide-transformations:用于磨砂实现
circleimageview:圆形图片view
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=”match_parent”
android:orientation=”vertical”
xmlns:app=”http://schemas.android.com/apk/res-auto”
>
<android.support.v7.widget.Toolbar
android:layout_width=”match_parent”
android:layout_height=”?attr/actionBarSize”
android:background=”@color/colorPrimary”
android:id=”@+id/toolbar_fragment_personal”
app:title=”个人中心”

>
</android.support.v7.widget.Toolbar>
<!–磨砂头像–>
<RelativeLayout

android:layout_below=”@+id/toolbar_fragment_personal”
android:id=”@+id/relative”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”>
<ImageView
android:id=”@+id/h_back”
android:layout_width=”match_parent”
android:layout_height=”180dp” />
<ImageView
android:id=”@+id/h_head”
android:layout_width=”80dp”
android:layout_height=”80dp”
android:layout_centerInParent=”true” />
<RelativeLayout
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:layout_alignBottom=”@id/h_back”
android:layout_marginBottom=”20dp”
android:orientation=”horizontal”>
<ImageView
android:id=”@+id/user_line”
android:layout_width=”1dp”
android:layout_height=”25dp”
android:layout_centerHorizontal=”true”
android:layout_marginLeft=”15dp”
android:background=”@android:color/white” />
<TextView
android:id=”@+id/user_name”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_toLeftOf=”@id/user_line”
android:text=”张三”
android:textColor=”@android:color/white”
android:textSize=”17sp” />
<TextView
android:id=”@+id/user_val”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_marginLeft=”15dp”
android:layout_toRightOf=”@id/user_line”
android:text=”182****5882″
android:textColor=”@android:color/white”
android:textSize=”17sp” />
</RelativeLayout>

</RelativeLayout>

<!–子项–>
<LinearLayout
android:id=”@+id/ll”
android:orientation=”vertical”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”>
<LinearLayout
android:layout_width=”match_parent”
android:orientation=”horizontal”
android:layout_height=”60dp”>
<de.hdodenhof.circleimageview.CircleImageView
android:src=”@mipmap/username”
android:layout_width=”wrap_content”
android:layout_height=”match_parent” />
<TextView
android:text=”用户名”
android:textSize=”20dp”
android:textColor=”#000000″
android:layout_weight=”1″
android:layout_marginLeft=”12dp”
android:gravity=”center_vertical”
android:layout_width=”wrap_content”
android:layout_height=”match_parent” />
<ImageView
android:layout_gravity=”center_vertical”
android:paddingRight=”20dp”
android:layout_width=”wrap_content”
android:src=”@drawable/youjiantou”
android:layout_height=”wrap_content” />
</LinearLayout>
<View
android:layout_width=”match_parent”
android:layout_height=”0.5dp”

android:background=”#090808″/>
<LinearLayout
android:layout_width=”match_parent”
android:orientation=”horizontal”
android:layout_height=”60dp”>
<de.hdodenhof.circleimageview.CircleImageView
android:src=”@mipmap/changepw”
android:layout_width=”wrap_content”
android:layout_height=”match_parent” />
<TextView
android:text=”修改密码”
android:textSize=”20dp”
android:textColor=”#000000″
android:layout_weight=”1″
android:layout_marginLeft=”12dp”
android:gravity=”center_vertical”
android:layout_width=”wrap_content”
android:layout_height=”match_parent” />
<ImageView
android:layout_gravity=”center_vertical”
android:paddingRight=”20dp”
android:layout_width=”wrap_content”
android:src=”@drawable/youjiantou”
android:layout_height=”wrap_content” />
</LinearLayout>
<View
android:layout_width=”match_parent”
android:layout_height=”0.5dp”

android:background=”#090808″/>
<LinearLayout
android:layout_width=”match_parent”
android:orientation=”horizontal”
android:layout_height=”60dp”>
<de.hdodenhof.circleimageview.CircleImageView
android:src=”@mipmap/sex”
android:layout_width=”wrap_content”
android:layout_height=”match_parent” />
<TextView
android:text=”性别”
android:textColor=”#000000″
android:textSize=”20dp”
android:layout_weight=”1″
android:layout_marginLeft=”12dp”
android:gravity=”center_vertical”
android:layout_width=”wrap_content”
android:layout_height=”match_parent” />
<ImageView
android:layout_gravity=”center_vertical”
android:paddingRight=”20dp”
android:layout_width=”wrap_content”
android:src=”@drawable/youjiantou”
android:layout_height=”wrap_content” />
</LinearLayout>
<View
android:layout_width=”match_parent”
android:layout_height=”0.5dp”

android:background=”#090808″/>
<LinearLayout
android:layout_width=”match_parent”
android:orientation=”horizontal”
android:layout_height=”60dp”>
<de.hdodenhof.circleimageview.CircleImageView
android:src=”@mipmap/version”
android:layout_width=”wrap_content”
android:layout_height=”match_parent” />
<TextView
android:text=”版本”
android:textSize=”20dp”
android:textColor=”#000000″
android:layout_weight=”1″
android:layout_marginLeft=”12dp”
android:gravity=”center_vertical”
android:layout_width=”wrap_content”
android:layout_height=”match_parent” />
<ImageView
android:layout_gravity=”center_vertical”
android:paddingRight=”20dp”
android:layout_width=”wrap_content”
android:src=”@drawable/youjiantou”
android:layout_height=”wrap_content” />
</LinearLayout>

</LinearLayout>

</LinearLayout>

在java文件中动态设置图片

//设置背景磨砂效果
Glide.with(this).load(R.drawable.touxiang)
.bitmapTransform(new BlurTransformation(getActivity(), 25), new CenterCrop(getActivity()))
.into(hBack);
//设置圆形图像
Glide.with(this).load(R.drawable.touxiang)
.bitmapTransform(new CropCircleTransformation(getActivity()))
.into(hHead);

Android Studio 开发 简易版音游APP

FREE  ——简易版音游APP
一、APP介绍
通过识别本地曲库,对音频文件进行识别提取出时间点,来产生滑块进行动态点击的畅玩过程,享受音乐的律动美感。界面主要仿照节奏大师等音游app,整体风格呈黑金色。(注:此app开发为课程作业,部分图片来自网图非原创,未曾商用)

 

二、APP特点
1.多种模式选择

常规设置:模式可选择双轨道或者四轨道;滑块数量可以选滑块较少或滑块较多;滑块速度可以选择慢速、中速、快速。

高级设置(开发人员选项):通过调整样本窗口大小、样本窗口数量、阈值权重等参数来调整滑块的数量。

2.支持本地所有MP3、WAV格式的音频

不同于曲库人为摆放滑块的位置,本app滑块位置是根据音频自动生成的,暂时配置有MP3、WAV格式音频文件的识别加载,能够较大程度地支持本地音频文件。

3.兼容不同分辨率的设备

通过android开发特有的dp单位适应不同分辨率手机的开发环境,通过ppi屏幕分辨率密度进行px与dp单位之间的转换,本APP能够适应ldpi、mdpi、hdpi、xhdpi、xxhdpi等不同分辨率密度,适配更多机型。

 

三、项目难点
1.滑块的滑动

通过比较Transaction、ObjectAnimator、ValueAnimator等动画效果,发现transaction只是表面移动,实际布局位置未改变,无法识别移动的位置,而且有的无法设置动画延迟时间,所以*后采用ObjectAnimatior为滑块滑动的主要部分。把滑块设为单独的EachButton、LandEachButton类,来动态设置它的动画起止位置,延迟时间等。这里动画延迟是结合音频节奏点的时间,延迟到那个节奏点的时候滑块动画才开始。

滑块的滑动主要用到的文件有Classes.EachButton、Classes.LandEachButton。

2.点击效果

这里点击时生成的best、good、miss字样主要是通过自定义Toast的setView方法实现,通过得到点击时该轨道列离得*近的滑块的位置与按钮位置之差d,得出best、good、miss指标的不同d的范围来得出点击评价,这里miss字样是在eachButton内部的onAnimationEnd()方法内部进行判别的。

点击效果主要用到的文件有toast.xml、toast_land.xml。

3.节奏点的识别

节奏点的识别主要用到音频采样、傅里叶变换(FFT)的知识,音频采样得到时域信号,这个信号可以看成是多个正弦波叠加的结果,通过傅里叶变换得到一段信号(一个样本窗口)里的关键频率,实现时域映射到频域,并与周围几个样本窗口的关键频率求均值加权得到阈值,大于阈值的信号点就可以看做节奏的起点,然后这个信号点的位置比例乘以总时间即为节奏点的时间,依此设置滑块延时。

实验过程中先是找到了一个wav格式文件画出波形图的样例,自己解读实现了一下,然后找到了mp3转化为wav格式的方法,然后放到android里面发现低于26的API不支持javax.sound等的包,于是又学习了mp3文件的格式,进行帧读取,后来发现大部分mp3文件是压缩过的,后来就仿照wav文件处理的代码自己实现了一个mp3文件的识别(有些数据流结构对不上mp3的标准帧格式,所以只是大致识别)。

节奏点识别主要用到的文件有WavHandle.WaveFileReader、Mp3Handle.Mp3FileReader、Classes.FFT 、Classes.HandleData。

4.兼容其他设备

由于activity中滑块位置的设置和获取是以px为单位的,所以需要转化为dp单位来兼容不同分辨率。通过得到设备的宽度getWindowManager().getDefaultDisplay().getWidth();对应不同的1dp=npx转换,其中对应关系(width,n)(240,0.75)(320,1.0)(480,1.5)(720,2.0)(1080,3.0)。

5.其他

申请读取内存的服务的实现。

通过MediaStore.Audio.Media.EXTERNAL_CONTENT_URI读取本地曲库,通过MediaStore.Audio.Media.DISPLAY_NAME等得到歌曲信息,通过RecycleView呈现歌单。其中用到的文件有Classes.Music、Classes.MusicAdapter。

通过广播、BaseActivity、ActivityCollector实现强行下线,避免重复打开活动。其中用到的文件有Classes.ActivityCollector、Classes.BaseActivity。

强制横屏的实现,通过layout_weight设置均分宽度居中。

 

四、APP界面
(注:此app开发为课程作业,图片来自网图非原创,未曾商用)

1.Logo

 

2.首页(MainActivity)

3.歌单页面(MusicViewActivity)

4.基础模式选择(OriginChoiceActivity)

5.高级设置(ChoiceActivity)

6.双轨道模式界面(GameActivity)

7.四轨道模型界面(FourGameActivity)

8.分数结果界面(ResultActivity)

六、改进空间
(时间限制,部分功能未曾实现)

1.暂停功能

2.连击效果

3.歌曲搜索功能

4.排行榜

5.人工控制节奏点

AndroidStudio实用教程:多界面跳转

1.先创建一个项目,项目文件如下:

在这里插入图片描述

2.activity_double_window.xml代码如下


<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".DoubleWindow">

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>

 

3.新建一个布局文件

在这里插入图片描述

4.修改好名字以后Finish
在这里插入图片描述

5.新建一个java类文件

在这里插入图片描述

6.修改好名字以后OK

在这里插入图片描述

7.刚创建的java类源码

package com.shawna.doublewindow;

import android.app.Activity;
import android.os.Bundle;

public class two extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.layout);
    }
}

 

8.在AndroidManifest.xml文件中增加类文件关联

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.shawna.doublewindow">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".DoubleWindow">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        //这一句 ↓
        <activity android:name=".two"></activity>
    </application>

</manifest>

 

9.在一开始创建的主页面上放一个按钮,按钮按下事件名称函数为play

在这里插入图片描述

10.DoubleWindow代码如下:

package com.shawna.doublewindow;

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;

public class DoubleWindow extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_double_window);
    }

    //按钮点击事件
    public void play(View view){
        //切换页面
        Intent intent = new Intent(DoubleWindow.this,two.class);
        startActivity(intent);
    }
}

 

 

Android Studio 点击按钮跳转新界面

问题描述
首先,我们有两个Java文件和与之绑定的xml文件。此处以HistoryActivity.java,activity_history.xml 和 EventDetail.java,activity_event_detail.xml为例。我们要实现在HistoryActivity界面中添加一个按钮,并且点击跳转到EventDetail界面。


为HistoryActivity界面添加按钮
在其对应的activity_history.xml 中:

<?xml version=”1.0″ encoding=”utf-8″?>
<android.support.constraint.ConstraintLayout xmlns:android=”http://schemas.android.com/apk/res/android”
xmlns:app=”http://schemas.android.com/apk/res-auto”
xmlns:tools=”http://schemas.android.com/tools”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
tools:context=”.HistoryActivity”>

<Button
android:id=”@+id/History”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:text=”Historical Event”
android:layout_alignParentLeft=”true”
android:layout_alignParentStart=”true”/>
</android.support.constraint.ConstraintLayout>

我们通过android:id=”@+id/History”语句讲button的id设置为History,在之后设置点击事件时使用。

为History按钮添加点击事件
在HistoryActivity.java中:

package com.example.xff.tm;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.content.Intent;
import android.widget.Button;
import android.widget.*;

public class HistoryActivity extends AppCompatActivity {
Button button = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_history);
button = (Button)findViewById(R.id.History);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setClass(HistoryActivity.this,EventDetail.class);
startActivity(intent);
}
});
}

}

通过之前定义的button的id来找到对应button,为之设置点击监听。当发生点击事件时,通过Intent进行跳转。
#在manifests->AndroidManifest.xml中添加activity(这个步骤通常是添加点击事件之后系统自动生成,可以进行检查)

<?xml version=”1.0″ encoding=”utf-8″?>
<manifest xmlns:android=”http://schemas.android.com/apk/res/android”
package=”com.example.xff.tm”>

<application
android:allowBackup=”true”
android:icon=”@mipmap/ic_launcher”
android:label=”@string/app_name”
android:roundIcon=”@mipmap/ic_launcher_round”
android:supportsRtl=”true”
android:theme=”@style/AppTheme”>
<activity android:name=”.HistoryActivity”>
<intent-filter>
<action android:name=”android.intent.action.MAIN” />

<category android:name=”android.intent.category.LAUNCHER” />
</intent-filter>
</activity>
<activity android:name=”.EventDetail”></activity>
</application>

</manifest>

EventDetail.java,activity_event_detail.xml
作为被跳转的界面,这两个文件只需要完成自己的功能即可:
EventDetail.java:

package com.example.xff.tm;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class EventDetail extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

setContentView(R.layout.activity_event_detail);
}
}

activity_event_detail.xml:

<?xml version=”1.0″ encoding=”utf-8″?>
<android.support.constraint.ConstraintLayout xmlns:android=”http://schemas.android.com/apk/res/android”
xmlns:app=”http://schemas.android.com/apk/res-auto”
xmlns:tools=”http://schemas.android.com/tools”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
tools:context=”.EventDetail”>

</android.support.constraint.ConstraintLayout>

华为跨代研发3nm芯片,命名麒麟9010…

因为受到制裁的影响,去年的麒麟9000发布之后,华为就表示这将会是麒麟旗舰芯片的*唱。而且在去年9月15号之后,各大代工厂都无法替华为代工芯片,这也就导致去年下半年发布的MATE 40系列迄今为止都处于缺货状态。


华为所面临的的困境并非只有芯片问题,在系统方面也受到了制约。好在软件和系统方面华为已经提前布局,鸿蒙已经开始适配手机,而且也有不少开发者加入到华为的阵营。但是说回到硬件问题,因为无论是华为自己还是国内厂商,都无法帮助华为度过这个难关。所以今年下半年的MATE 50系列是否可以顺利推出还是一个悬念。

虽然硬件无法制造,这并不意味着华为就此放弃了芯片的研发工作。根据*新爆料显示华为新一代的麒麟芯片还在研发当中,并且会直接使用3nm工艺,暂时命名为麒麟9010。

要知道研发芯片是一件很费钱的事情,之前华为推出麒麟芯片还可以卖出去回血,现在就只能拿来练手了,以保证自己的业务不生疏。所以华为研发出来的新一代芯片只能测试小量验证流片,并不能代工生产。


今年新一代的工艺将会是5nm+,明年才会是3nm,所以华为应该是直接跳过了5nm+工艺,跨代研发了。要知道去年的麒麟9000提升非常大,性能上与骁龙888的差距微乎其微。现在华为并没有放弃研发工作,将来有一天解决了代工问题,相信依旧会处于手机芯片的一流行列。

此外,华为麒麟团队还发布了新年致辞。从2009年的K3V2,到2020年的麒麟9000,华为经历了十年风雨,十年征程。未来,也将会不忘初心,砥砺前行。相信唯坚持,得突破。


除了华为自身没有放弃芯片的研发设计,国内的光刻机技术也在稳步的发展当中。虽然这个进度非常艰难,但是还是有些进展的。目前新型材料工艺也在紧罗密布的探讨研发中,根据消息透露,未来2-3年光刻机或者是碳基芯片领域必有一个会突破。

我们不清楚华为以及国内的困境还要持续多久,就像麒麟团队说的那样唯坚持,得突破。 也希望华为能够一直坚持下去。想获取更多科技资讯,快来关注我们吧!

iOS 开发过程中遇到的那些奇葩的坑

1.打印异常:dyld: lazy symbol binding failed: Symbol not found: _objc_unsafeClaimAutoreleasedReturnValue(

dyld_sim`dyld_fatal_error:

0x60e000 <+0>: int3

->  0x60e001 <+1>: nop


问题描述:本人开发了一个iOS项目,开发环境:电脑环境Mac OS 10.12.2,编译环境:Xcode8.2.1,测试环境:iOS 10.2.1、iOS 8.4以及iOS 9.3.5 。应用程序iOS 10 和iOS 9.3.5环境运行正常,但是在iOS 8.4运行不起来,直接闪退,如图所示:

问题解决办法:

检查调用的静态库,查看静态库*低兼容版本(Department Target)是多少,如果是静态库*低兼容版本比你项目的兼容版本高,就需要修改之后重新打包静态库,我的问题就是 公司的SDK开发人员打包静态库的时候是用Xcode 7.2(默认*低兼容版本未iOS 9.2)打包的,忘记改*低兼容版本了,被坑了好几天,终于找到问题了

2.程序编译正常、运行正常但是打包时候报错,异常信息如下:

Undefined symbols for architecture armv7:

“_OBJC_CLASS_$_*****”, referenced from:

objc-class-ref in ********.o

ld: symbol(s) not found for architecture armv7

clang: error: linker command failed with exit code 1 (use -v to see invocation)

如果你的项目也是运行正常,但是打包出问题了就可能是Build Active Architecture Only这个属性的问题,如下图所示:

问题就出在这里,Debug模式下Build Active Architecture Only设置为YES,意思是只编译当前设备所对应的系统环境下,如果设置为NO,意思是编译为你所包含的所有设备的系统环境下通用的类型,这时候就出问题了!如果是为了发布而打包应用,这时候就不能把这个键值对设置为YES,这会导致你的应用上架后在其他设备不同系统上出现问题甚至无法运行的后果,其实出现问题的原因,上边的异常信息已经显示出来了,就是红色箭头指示的地方

我这边的原因是公司封装SDK的人员在打包静态库的时候忘记修改这个键值对(默认的是Debug模式为YES,Release模式为NO),要把生成静态库的方法(运行或者编译)设置为NO,修改后重新替换即可

iOS截屏功能的实现

使用场景:

本人在自定义UITabBarController的时候,想要实现手势滑动返回上一页面的效果,也就是仿QQ的滑动返回,这种滑动返回是基于UIPanGestureRecognizer这一手势,实现原理是在导航条Push的时候截取当前屏幕,并把截取到的图片添加的Window的视图上,让后当启用滑动手势的时候只要把当前(Push到的)视图滑动开来,就可以看到之前截取到的倾慕快照,当返回上一页面的时候再把截取的图片从Window上移除即可。遇到的问题是:小编自定义的UITabBarController是继承于UIViewControler,而不是系统的UITabBarController,底部的按钮区域是一个View,截取屏幕的时候总是不能截取到按钮区域的View;这就是*种截屏方法,而后通过查找资料找到一种能够解决此问题的方法,也就是第二种截屏方法。

*种截屏方法:

//获取截屏图片
– (UIImage *)getCurrentScreenShot{

UIGraphicsBeginImageContextWithOptions([[[UIApplication sharedApplication] keyWindow] bounds].size, NO, 0.0);
[[[UIApplication sharedApplication] keyWindow].layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

return image;
}

注:*种截图方法适合于屏幕上只有一个View的时候,效率较高,一般场景下就够用了。原理是首先创建一个和主屏幕(KeyWindow)一样大小的画布,然后通过 renderInContext方法把屏幕图层上的内容绘制到画布上,然后根据这张画布生成图片即可。

第二种截屏方法:

//截取全屏视图
-(UIImage *)getImageWithFullScreenshot
{

UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
CGSize imageSize = CGSizeZero;

//适配横屏状态
if (UIInterfaceOrientationIsPortrait(orientation) )
imageSize = [UIScreen mainScreen].bounds.size;
else
imageSize = CGSizeMake([UIScreen mainScreen].bounds.size.height, [UIScreen mainScreen].bounds.size.width);

UIGraphicsBeginImageContextWithOptions(imageSize, NO, [UIScreen mainScreen].scale);
CGContextRef context = UIGraphicsGetCurrentContext();

for (UIWindow *window in [[UIApplication sharedApplication] windows])
{
CGContextSaveGState(context);
CGContextTranslateCTM(context, window.center.x, window.center.y);
CGContextConcatCTM(context, window.transform);
CGContextTranslateCTM(context, -window.bounds.size.width * window.layer.anchorPoint.x, -window.bounds.size.height * window.layer.anchorPoint.y);

// Correct for the screen orientation
if(orientation == UIInterfaceOrientationLandscapeLeft)
{
CGContextRotateCTM(context, (CGFloat)M_PI_2);
CGContextTranslateCTM(context, 0, -imageSize.width);
}
else if(orientation == UIInterfaceOrientationLandscapeRight)
{
CGContextRotateCTM(context, (CGFloat)-M_PI_2);
CGContextTranslateCTM(context, -imageSize.height, 0);
}
else if(orientation == UIInterfaceOrientationPortraitUpsideDown)
{
CGContextRotateCTM(context, (CGFloat)M_PI);
CGContextTranslateCTM(context, -imageSize.width, -imageSize.height);
}

if([window respondsToSelector:@selector(drawViewHierarchyInRect:afterScreenUpdates:)])
[window drawViewHierarchyInRect:window.bounds afterScreenUpdates:NO];
else
[window.layer renderInContext:UIGraphicsGetCurrentContext()];

CGContextRestoreGState(context);
}

UIImage *image = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

return image;
}

注:第二种截屏方法适用于屏幕上有多个视图的场景,原理基本和*种方法一样,只是第二种方法不仅会绘制主屏幕的视图,它还会遍历已存在的所有window,并拼接所得到的画布。

附:此两种方法是适用于iOS 7及以后版本,除此之外还有一些过期的方法和私有API,小编是不建议使用的,在此也就不再粘贴代码,如有问题可留言告知,多谢!

iOS中正确的截屏姿势

iOS中正确的截屏姿势

*种

这是iOS 3时代开始就被使用的方法,它被废止于iOS 7。iOS的私有方法,效率很高。

1
2
3
4
5
6
7
#import
extern  "C"  CGImageRef UIGetScreenImage();
UIImage * screenshot(void) NS_DEPRECATED_IOS(3_0,7_0);
UIImage * screenshot(){
     UIImage *image = [UIImage imageWithCGImage:UIGetScreenImage()];
     return  image;
}

第二种

这是在比较常见的截图方法,不过不支持Retina屏幕。

1
2
3
4
5
6
7
8
UIImage * screenshot(UIView *);
UIImage * screenshot(UIView *view){
     UIGraphicsBeginImageContext(view.frame.size);
     [view.layer renderInContext:UIGraphicsGetCurrentContext()];
     UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
     UIGraphicsEndImageContext();
     return  image;
}

第三种

从iPhone 4、iPod Touch 4开始,Apple逐渐采用Retina屏幕,于是在iOS 4的SDK中我们有了,上面的截图方法也自然变成了这样。

1
2
3
4
5
6
7
8
9
10
11
12
13
UIImage * screenshot(UIView *) NS_AVAILABLE_IOS(4_0);
UIImage * screenshot(UIView *view){
     if (UIGraphicsBeginImageContextWithOptions != NULL)
     {
         UIGraphicsBeginImageContextWithOptions(view.frame.size, NO, 0.0);
     else  {
         UIGraphicsBeginImageContext(view.frame.size);
     }
     [view.layer renderInContext:UIGraphicsGetCurrentContext()];
     UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
     UIGraphicsEndImageContext();
     return  image;
}

第四种

或许你会说有时Hook的是一个按钮的方法,用第三个方法的话,根本找不到view来传值,不过还好,iOS 7又提供了一些UIScreen的API。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
UIImage * screenshot(void) NS_AVAILABLE_IOS(7_0);
UIImage * screenshot(){
     UIView * view = [[UIScreen mainScreen] snapshotViewAfterScreenUpdates:YES];
     if (UIGraphicsBeginImageContextWithOptions != NULL)
     {
         UIGraphicsBeginImageContextWithOptions(view.frame.size, NO, 0.0);
     else  {
         UIGraphicsBeginImageContext(view.frame.size);
     }
     [view.layer renderInContext:UIGraphicsGetCurrentContext()];
     UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
     UIGraphicsEndImageContext();
     return  image;
}

第五种

1
2
3
4
@interface SBScreenShotter : NSObject
+ (id)sharedInstance;
- (void)saveScreenshot:(_Bool)arg1;
@end

然后直接

1
[[SBScreenShotter sharedInstance] saveScreenshot:YES];

一道白光之后,咱们就模拟了用户截屏的动作,不过这个方法在只需要截屏时比较好,如果要对屏幕录像(其实就是不断截图)的话,那不得闪瞎了。。而且我们也拿不到UIImage的实例去拼成一个视频呀。即使通过Hook别的类拿到UIImage的实例,这个私有API的效率大概也是达不到30FPS的视频要求的。

那么现在我们有5种方法了,*种是私有API,私有API通常效率和质量都比Documented API的好,可是它在iOS 7以后就被废除了啊,就没有别的了吗?

答案当然是————有的!用Private Framework来完成这项任务!直接走底层拿屏幕的缓冲数据,然后生成UIImage的实例。

第六种

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#import #import #import #import #import extern "C" IOReturn IOSurfaceLock(IOSurfaceRef buffer, uint32_t options, uint32_t *seed);
extern  "C"  IOReturn IOSurfaceUnlock(IOSurfaceRef buffer, uint32_t options, uint32_t *seed);
extern  "C"  size_t IOSurfaceGetWidth(IOSurfaceRef buffer);
extern  "C"  size_t IOSurfaceGetHeight(IOSurfaceRef buffer);
extern  "C"  IOSurfaceRef IOSurfaceCreate(CFDictionaryRef properties);
extern  "C"  void *IOSurfaceGetBaseAddress(IOSurfaceRef buffer);
extern  "C"  size_t IOSurfaceGetBytesPerRow(IOSurfaceRef buffer);
extern const CFStringRef kIOSurfaceAllocSize;
extern const CFStringRef kIOSurfaceWidth;
extern const CFStringRef kIOSurfaceHeight;
extern const CFStringRef kIOSurfaceIsGlobal;
extern const CFStringRef kIOSurfaceBytesPerRow;
extern const CFStringRef kIOSurfaceBytesPerElement;
extern const CFStringRef kIOSurfacePixelFormat;
enum
{
     kIOSurfaceLockReadOnly  =0x00000001,
     kIOSurfaceLockAvoidSync =0x00000002
};
UIImage * screenshot(void);
UIImage * screenshot(){
     IOMobileFramebufferConnection connect;
     kern_return_t result;
CoreSurfaceBufferRef screenSurface = NULL;
     io_service_t framebufferService = IOServiceGetMatchingService(kIOMasterPortDefault,IOServiceMatching( "AppleH1CLCD" ));
if (!framebufferService)
         framebufferService = IOServiceGetMatchingService(kIOMasterPortDefault,IOServiceMatching( "AppleM2CLCD" ));
if (!framebufferService)
         framebufferService = IOServiceGetMatchingService(kIOMasterPortDefault,IOServiceMatching( "AppleCLCD" ));
result = IOMobileFramebufferOpen(framebufferService, mach_task_self(), 0, &connect);
result = IOMobileFramebufferGetLayerDefaultSurface(connect, 0, &screenSurface);
     uint32_t aseed;
     IOSurfaceLock((IOSurfaceRef)screenSurface, 0x00000001, &aseed);
     size_t width = IOSurfaceGetWidth((IOSurfaceRef)screenSurface);
     size_t height = IOSurfaceGetHeight((IOSurfaceRef)screenSurface);
     CFMutableDictionaryRef dict;
size_t pitch = width*4, size = width*height*4;
     int bPE=4;
     char pixelFormat[4] = { 'A' , 'R' , 'G' , 'B' };
     dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
     CFDictionarySetValue(dict, kIOSurfaceIsGlobal, kCFBooleanTrue);
     CFDictionarySetValue(dict, kIOSurfaceBytesPerRow, CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt32Type, &pitch));
     CFDictionarySetValue(dict, kIOSurfaceBytesPerElement, CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt32Type, &bPE));
     CFDictionarySetValue(dict, kIOSurfaceWidth, CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt32Type, &width));
     CFDictionarySetValue(dict, kIOSurfaceHeight, CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt32Type, &height));
     CFDictionarySetValue(dict, kIOSurfacePixelFormat, CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt32Type, pixelFormat));
     CFDictionarySetValue(dict, kIOSurfaceAllocSize, CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt32Type, &size));
     IOSurfaceRef destSurf = IOSurfaceCreate(dict);
     IOSurfaceAcceleratorRef outAcc;
     IOSurfaceAcceleratorCreate(NULL, 0, &outAcc);
     IOSurfaceAcceleratorTransferSurface(outAcc, (IOSurfaceRef)screenSurface, destSurf, dict,NULL);
     IOSurfaceUnlock((IOSurfaceRef)screenSurface, kIOSurfaceLockReadOnly, &aseed);
CFRelease(outAcc);
     CGDataProviderRef provider =  CGDataProviderCreateWithData(NULL,  IOSurfaceGetBaseAddress(destSurf), (width * height * 4), NULL);
     CGImageRef cgImage = CGImageCreate(width, height, 8,
8*4, IOSurfaceGetBytesPerRow(destSurf),
  CGColorSpaceCreateDeviceRGB(), kCGImageAlphaNoneSkipFirst |kCGBitmapByteOrder32Little,provider, NULL, YES, kCGRenderingIntentDefault);
     UIImage *image = [UIImage imageWithCGImage:cgImage];
     return  image;
}

需要注意的是,第五种方法需要修改一下IOMobileFramebuffer的头文件。

1
typedef void * IOMobileFramebufferConnection;

In the reversed header, IOMobileFramebufferConnection is typedef’d to io_connect_t, which is typedef’d to io_object_t, which is mach_port_t, which is __darwin_mach_port_t, which is __darwin_mach_port_name_t, which is __darwin_natural_t, which is unsigned int! Int just happens to be pointer-sized on 32-bit, but is not under 64-bit。

——StackoverFlow

修改好的头文件顺便也丢上来吧,解压后放在Project的根目录下。

如果你使用的是theos的话,记得在Makefile里写上,

YOUR_TWEAK_NAME_PRIVATE_FRAMEWORKS = IOSurface IOKit IOMobileFramebuffer

YOUR_TWEAK_NAME_CFLAGS = -I./headers/ -I./headers/IOSurface

如果是XCode上的Logos Tweak的话,在Build Settings -> Search Paths -> Header Search Paths里面添加一项:$(PROJECT_DIR)/YOUR_PROJECT_NAME/headers, 搜索方式为recursive. *后在Build Phases里Link上IOSurface IOKit IOMobileFramebuffer这三个私有Framework。

iOS开发 调试 网络限速

在iOS开发中,针对不同网络状况做一下测试处理是很有必要的。但是我发现还是有一些iOS开发者不太注意到不同网络环境下的调试问题,或者说不清楚如何调试这种情况。

下面我将针对真机和模拟器分别做简单的说明。告诉大家如何模拟不同的网络状况。

1.真机情况下

其实Apple在iOS系统中预置了网络调试工具,但是只有添加过测试设备,并使用XCode连接下设备才能激活.

这时只要去设置中就可以看到多出来一项:开发者

注意中间的那行NETWORK LINK CONDITIONER

上图中的Status是表示网络限制是否开启,点击进去就可看到详细设置

可以看到系统默认配置的网络条件还是很多的,其中:

100%Loss是全丢包,

3G这个大家应该都知道

DSL是电话线上网,

Edge是2G网络,

High Latency DNS是高延迟,

Very Bad Network是网络状况不稳定。

并且底部还有一个添加额外配置的选项可以自定义网络状况。

PS:这项开启后影响的是整个系统,所以调试完毕后不要忘记关掉,免得奇怪网络状态咋不正常了。。。

2.模拟器

由于模拟器是在电脑上的,mac上也有和iOS设备中一样的调试工具,并且被收录在Apple官网工具中。同样,这工具也是影响整个系统的。

详情可看:http://nshipster.com/network-link-conditioner/

该工具的Apple官方地址:https://developer.apple.com/downloads/index.action?q=Network%20Link%20Conditioner#

具体的步骤:

下载:

打开DMG文件,里面包含了许多调试工具,其他的工具有兴趣的可以自己去看一下。这里我们只打开Network Link Conditioner.prefPane这个文件

在系统偏好中出现了工具:

打开工具,看一下,几乎和手机上是一样的。

*后:

希望各位看完这篇文章,能更好的做好应用的各种调试,开发出更加稳定可靠的应用来。

iOS 播放flash视频文件

找一个相对比较简单的swf,在WIN下面用闪客精灵7打开。
(这个软件可以实现反编译swf和提取swf资源等,破解版找了很久,方便大家就直接发上来了)
http://115.com/file/ankbb0zw


这里我选了一个比较简单的倒计时swf

然后,用闪客精灵的swf转html5功能把这个swf转成html5

然后可以看到一个html和一个js,打开那个html就可以看到动画

但是这个js看着很烦,而且xcode会把js当作源文件编译,所以先要把那个js和那个html合并掉
用DreamWeaver打开那个html和js,在html文件里面找到这样的代码

这个代码用于引用这个js,接下来把它换成这样

然后把那个js里面的所有代码粘到中间,就成这样

接下来就可以把这个js删掉了,html照样可以播放

然后,换到Mac系统,用xcode新建一个单界面项目,在界面里面放入一个uiwebview

再修改试图控制器代码:

复制代码

  1. //ViewController.h
  2. #import <UIKit/UIKit.h>
  3. @interface ViewController : UIViewController{
  4.     IBOutlet UIWebView *myWebView;
  5. }
  6. @property(retain,nonatomic)IBOutlet UIWebView *myWebView;
  7. @end

 

复制代码

  1. //ViewController.m
  2. #import “ViewController.h”
  3. @implementation ViewController
  4. @synthesize myWebView;
  5. – (void)viewDidLoad
  6. {
  7.     [super viewDidLoad];
  8.     [self loadDocument:@”Flash.html”];
  9. }
  10. -(void)dealloc{
  11.     [super dealloc];
  12.     [myWebView release];
  13. }
  14. – (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
  15. {
  16.     return YES;
  17. }
  18. -(void)loadDocument:(NSString*)htmlName{
  19.     NSString *bundlePath=[[NSBundle mainBundle]bundlePath];
  20.     NSString *path=[bundlePath stringByAppendingPathComponent:htmlName];
  21.     NSURL *url=[NSURL fileURLWithPath:path];
  22.     NSURLRequest *request=[NSURLRequest requestWithURL:url];
  23.     NSLog(@”%@”,url);
  24.     self.myWebView.scalesPageToFit=YES;
  25.     [self.myWebView loadRequest:request];
  26. }
  27. @end

*后,把那个html的名称改成“Flash.html”,当作资源放入项目,一切OK啦。。。。。。。。
虽然比较牵强,但是至少还是实现了哈,原创帖欢迎转载,如有问题可以回复提出。我才初一,希望大哥大姐们可以体谅我。。。

附上Demo  IOS_Flash.zip (99 K)

1.将swf视频转换成html。–闪客精灵

会出现两个文件(html,js)

2.在html文件*后面添加上

</script>

<script>

此处把js的内容全部粘帖过来即可

</script>

</body>

</html>

 

3.

UIWebView * webView = [[UIWebViewalloc]initWithFrame:CGRectMake(10,50, 300, 250)];

NSString *bundlePath=[[NSBundle mainBundle]bundlePath];

NSString *path=[bundlePathstringByAppendingPathComponent:htmlName];

NSURL *url=[NSURL fileURLWithPath:path];

NSURLRequest *request=[NSURLRequestrequestWithURL:url];

NSLog(@”%@”,url);

[webView setDelegate:self];

webView.scalesPageToFit=YES;

[webView loadRequest:request];

[self.viewaddSubview:webView];

友情链接: SITEMAP | 旋风加速器官网 | 旋风软件中心 | textarea | 黑洞加速器 | jiaohess | 老王加速器 | 烧饼哥加速器 | 小蓝鸟 | tiktok加速器 | 旋风加速度器 | 旋风加速 | quickq加速器 | 飞驰加速器 | 飞鸟加速器 | 狗急加速器 | hammer加速器 | trafficace | 原子加速器 | 葫芦加速器 | 麦旋风 | 油管加速器 | anycastly | INS加速器 | INS加速器免费版 | 免费vqn加速外网 | 旋风加速器 | 快橙加速器 | 啊哈加速器 | 迷雾通 | 优途加速器 | 海外播 | 坚果加速器 | 海外vqn加速 | 蘑菇加速器 | 毛豆加速器 | 接码平台 | 接码S | 西柚加速器 | 快柠檬加速器 | 黑洞加速 | falemon | 快橙加速器 | anycast加速器 | ibaidu | moneytreeblog | 坚果加速器 | 派币加速器 | 飞鸟加速器 | 毛豆APP | PIKPAK | 安卓vqn免费 | 一元机场加速器 | 一元机场 | 老王加速器 | 黑洞加速器 | 白石山 | 小牛加速器 | 黑洞加速 | 迷雾通官网 | 迷雾通 | 迷雾通加速器 | 十大免费加速神器 | 猎豹加速器 | 蚂蚁加速器 | 坚果加速器 | 黑洞加速 | 银河加速器 | 猎豹加速器 | 海鸥加速器 | 芒果加速器 | 小牛加速器 | 极光加速器 | 黑洞加速 | movabletype中文网 | 猎豹加速器官网 | 烧饼哥加速器官网 | 旋风加速器度器 | 哔咔漫画 | PicACG | 雷霆加速