0%

这个Demo是用前文的Lame库写的,分两步走,第一步使用raw格式录音,第二步转换。部分代码抄自三星的demo. android lame库:Android开发 使用Lame把音频文件转换成mp3格式

package com.pocketdigi.recordermp3;

import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

import android.app.Activity;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;

import com.pocketdigi.utils.FLameUtils;

public class MainActivity extends Activity {
    
    Button record,raw2mp3;
    boolean isRecording=false;
    private short[] mBuffer;
    AudioRecord mRecorder;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        record=(Button)findViewById(R.id.record);
        raw2mp3=(Button)findViewById(R.id.raw2mp3);
        initRecorder();
        final String tempFile=Environment.getExternalStorageDirectory()+"/aa.raw";
        final String mp3File=Environment.getExternalStorageDirectory()+"/bb.mp3";
        record.setOnClickListener(new OnClickListener() {
            
            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                if(isRecording)
                {
                    record.setText("录音");
                    isRecording=false;
                    mRecorder.stop();
                }else{
                    record.setText("停止");
                    isRecording=true;
                    mRecorder.startRecording();
                    startBufferedWrite(new File(tempFile));
                }
            }
        });
        raw2mp3.setOnClickListener(new OnClickListener() {
            
            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                if(isRecording)
                {
                    return;
                }
                File file=new File(tempFile);
                if(!file.exists())
                {
                    return;
                }
                new Thread()
                {
                    public void run()
                    {
                        FLameUtils lameUtils=new FLameUtils(1, 16000, 96);
                        System.out.println(lameUtils.raw2mp3(tempFile, mp3File));
                    }
                }.start();
                
            }
        });

    }
    /**
     * 初始化Recorder
     */
    public void initRecorder()
    {
        int bufferSize = AudioRecord.getMinBufferSize(16000, AudioFormat.CHANNEL_IN_MONO,
                AudioFormat.ENCODING_PCM_16BIT);
        mBuffer = new short[bufferSize];
        mRecorder = new AudioRecord(MediaRecorder.AudioSource.MIC, 16000, AudioFormat.CHANNEL_IN_MONO,
                AudioFormat.ENCODING_PCM_16BIT, bufferSize);
    }
    /**
     * 写入到文件
     * @param file
     */
    private void startBufferedWrite(final File file) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                DataOutputStream output = null;
                try {
                    output = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
                    while (isRecording) {
                        int readSize = mRecorder.read(mBuffer, 0, mBuffer.length);
                        for (int i = 0; i < readSize; i++) {
                            output.writeShort(mBuffer[i]);
                        }
                    }
                } catch (IOException e) {
                    Toast.makeText(MainActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show();
                } finally {
                    if (output != null) {
                        try {
                            output.flush();
                        } catch (IOException e) {
                            Toast.makeText(MainActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show();
                        } finally {
                            try {
                                output.close();
                            } catch (IOException e) {
                                Toast.makeText(MainActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show();
                            }
                        }
                    }
                }
            }
        }).start();
    }
    @Override
    protected void onDestroy() {
        // TODO Auto-generated method stub
        super.onDestroy();
        mRecorder.release();
    }

}

Android录音默认不支持mp3格式,为了生成mp3,可以录制raw格式,再使用lame转换成mp3.Lame是用C语言写的,所以需要NDK编译。编译方法: http://developer.samsung.com/android/technical-docs/Porting-and-using-LAME-MP3-on-Android-with-JNI, 按这篇文章,一次通过。英文也简单,所以就不翻译了,为了方便懒人以及编译没成功的同学,我把编译好的库打了个包,调用很方便。 下载地址: http://vdisk.weibo.com/s/sHZQR 使用方法:把armeabi目录和flame.jar放到项目的libs目录下.在需要的位置:

    FLameUtils lameUtils=new FLameUtils(1, 16000, 96);
    lameUtils.raw2mp3(Environment.getExternalStorageDirectory()+"/20130306172218.raw", Environment.getExternalStorageDirectory()+"/bbbb.mp3");

sql中的count用于统计符合条件的记录总数,下面是在ormlite中的使用方法:

    /**
     * @return 返回未读消息总数
     */
    public long getUnReadCount() {
        QueryBuilder qb = messageDao.queryBuilder();
        try {
            qb.where().eq("readed", false);
            qb.setCountOf(true);
            return messageDao.countOf(qb.prepare());
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return 0l;
    }

在做一个IM,需要在收到消息或者发送消息时,跳到聊天记录的最底部,就是刚发或者收的那条消息上。 用setSelection方法,有时候会没反应,不好使,用smoothScrollToPosition有时候跳到的不是最下面的那条,还是倒数第二条,暂没找到原因在哪。最后使用setTranscriptMode方法解决。 void android.widget.AbsListView.setTranscriptMode(int mode) mode设为AbsListView.TRANSCRIPT_MODE_ALWAYS_SCROLL,即可在插入item时自动跳到底部。另外,如果往顶上插,是不会跳的,刚好实现了我想要的功能,上拉刷新,停在当前位置。

第一、在根目录下的Android.mk里添加log库. LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := TestFFmpegJni LOCAL_SRC_FILES := TestFFmpegJni.c LOCAL_C_INCLUDES += $(LOCAL_PATH)/ffmpeg LOCAL_LDLIBS += -L$(LOCAL_PATH)/so -lffmpeg LOCAL_LDLIBS += -llog include $(BUILD_SHARED_LIBRARY) 上面是加上ffmpeg的,主要是LOCAL_LDLIBS += -llog这行。 c文件里,导入h,编写宏

#include 
#define LOG_TAG "JNI"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)

使用时:

LOGI("在c输出");

支持类似于printf的不定参数。

Google官方文档没写要设置ANDROID_PRODUCT_OUT环境变量,执行fastboot flashall -w时报错,大致是未加-p参数也未设置ANDROID_PRODUCT_OUT。 设置一下,就可以了: export ANDROID_PRODUCT_OUT=/home/fhp/android/out/target/product/maguro 因为我的机器是Galaxy Nexus,所以最后的目录是maguro,如果是模拟器,应该是generic. 另外fastboot命令必须用root权限,否则会一直停在waiting for device

PopupWindow没有显示在指定控件上方的方法,只好自己定义类继承PopupWindow来实现。有一点需要注意的是,在Android中,所有的View都必须在显示以后才获取到正确的高度和宽度,而PopupWindow定位使用的是设置PopupWindow的左上角座标,所以,我们在获取到指定控件在屏幕中的座标后,需要加上PopupWindow的高度,所以,这个方法有一定的限制,PopupWindow的高度必须是确定的,或者说,必须先计算出即将显示出来的PopupWindow的高度。

package com.pocketdigi.views;

import android.content.Context;
import android.view.Gravity;
import android.view.View;
import android.widget.PopupWindow;
import cn.jtang.discussion.R;

public class CustomPopupWindow extends PopupWindow {
    public CustomPopupWindow(View contentView, int width, int height) {
        super(contentView, width, height, false);
    }
    /**
     * 在指定控件上方显示,默认x座标与指定控件的中点x座标相同
     * @param anchor
     * @param xoff
     * @param yoff
     */
    public void showAsPullUp(View anchor, int xoff, int yoff)
    {
        //保存anchor在屏幕中的位置
        int[] location=new int[2];
        //保存anchor上部中点
        int[] anchorCenter=new int[2];
        //读取位置anchor座标
        anchor.getLocationOnScreen(location);
        //计算anchor中点
        anchorCenter[0]=location[0]+anchor.getWidth()/2;
        anchorCenter[1]=location[1];
        super.showAtLocation(anchor, Gravity.TOP|Gravity.LEFT, anchorCenter[0]+xoff, anchorCenter[1]-anchor.getContext().getResources().getDimensionPixelSize(R.dimen.popup_upload_height)+yoff);
    }
}

PopupWindow显示的方法有三个,showAsDropDown(anchor),showAsDropDown(anchor, xoff, yoff)和showAtLocation(parent, gravity, x, y)。 前两个showAsDropDown方法是让PopupWindow相对于某个控件显示,而showAtLocation是相对于整个窗口的。 第一个参数是View类型的parent,虽然这里参数名是parent,其实,不是把PopupWindow放到这个parent里,并不要求这个parent是一个ViewGroup,这个参数名让人误解。官方文档”a parent view to get the android.view.View.getWindowToken() token from “,这个parent的作用应该是调用其getWindowToken()方法获取窗口的Token,所以,只要是该窗口上的控件就可以了。 第二个参数是Gravity,可以使用|附加多个属性,如Gravity.LEFT|Gravity.BOTTOM。 第三四个参数是x,y偏移。

gsettings set org.gnome.gedit.preferences.encodings auto-detected "['UTF-8','GB18030','GB2312','GBK','BIG5','CURRENT','UTF-16']"